From b2bd97c16e507627f4345eb0e339cdb939d9590d Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 15 Sep 2025 12:31:15 -0600 Subject: [PATCH] TEMP --- crates/eval/src/eval.rs | 5 +- crates/settings/src/settings.rs | 7 +- crates/settings/src/settings_content.rs | 367 ++++++++++++++++++++- crates/settings/src/settings_store.rs | 350 +++++++++++--------- crates/theme/src/settings.rs | 2 - crates/title_bar/src/title_bar_settings.rs | 76 ++--- crates/util/src/util.rs | 18 + crates/zeta_cli/src/headless.rs | 5 +- 8 files changed, 618 insertions(+), 212 deletions(-) diff --git a/crates/eval/src/eval.rs b/crates/eval/src/eval.rs index 32399e26c020b315f948e7e9013c7000ef9bd899..17b9486a9f5cbbc8f0cafe86f55a6ac00d1c3023 100644 --- a/crates/eval/src/eval.rs +++ b/crates/eval/src/eval.rs @@ -340,10 +340,7 @@ pub fn init(cx: &mut App) -> Arc { release_channel::init(app_version, cx); gpui_tokio::init(cx); - let mut settings_store = SettingsStore::new(cx); - settings_store - .set_default_settings(settings::default_settings().as_ref(), cx) - .unwrap(); + let mut settings_store = SettingsStore::new(cx, &settings::default_settings()); cx.set_global(settings_store); client::init_settings(cx); diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index c02ea63e36aff49fc8b6b61423367cf862b9e544..41302a430441107dd7c14f9c9c121be84a0012ba 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -8,6 +8,8 @@ mod settings_store; mod settings_ui_core; mod vscode_import; +pub use settings_content::*; + use gpui::{App, Global}; use rust_embed::RustEmbed; use std::{borrow::Cow, fmt, str}; @@ -76,10 +78,7 @@ impl fmt::Display for WorktreeId { pub struct SettingsAssets; pub fn init(cx: &mut App) { - let mut settings = SettingsStore::new(cx); - settings - .set_default_settings(&default_settings(), cx) - .unwrap(); + let settings = SettingsStore::new(cx, &default_settings()); cx.set_global(settings); BaseKeymap::register(cx); SettingsStore::observe_active_settings_profile_name(cx).detach(); diff --git a/crates/settings/src/settings_content.rs b/crates/settings/src/settings_content.rs index d5c11da66767a8016509d31d81a7f5b0754904bd..03510564d117d73db97b32d30b634c9f6c253b71 100644 --- a/crates/settings/src/settings_content.rs +++ b/crates/settings/src/settings_content.rs @@ -3,8 +3,9 @@ use std::env; use std::num::NonZeroU32; use std::sync::Arc; +use anyhow::Result; use collections::{HashMap, HashSet}; -use gpui::{App, Modifiers, SharedString}; +use gpui::{App, FontFallbacks, FontFeatures, HighlightStyle, Hsla, Modifiers, SharedString}; use release_channel::ReleaseChannel; use schemars::{JsonSchema, json_schema}; use serde::de::{self, IntoDeserializer, MapAccess, SeqAccess, Visitor}; @@ -20,6 +21,10 @@ pub struct SettingsContent { pub project: ProjectSettingsContent, pub base_keymap: Option, + + pub auto_update: Option, + + pub title_bar: Option, } impl SettingsContent { @@ -32,7 +37,7 @@ impl SettingsContent { #[derive(Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct ServerSettingsContent { #[serde(flatten)] - project: ProjectSettingsContent, + pub project: ProjectSettingsContent, } #[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)] @@ -970,3 +975,361 @@ pub enum IndentGuideBackgroundColoring { /// Use a different color for each indentation level. IndentAware, } + +#[derive(Copy, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, Debug)] +pub struct TitleBarSettingsContent { + /// Controls when the title bar is visible: "always" | "never" | "hide_in_full_screen". + /// + /// Default: "always" + pub show: Option, + /// Whether to show the branch icon beside branch switcher in the title bar. + /// + /// Default: false + pub show_branch_icon: Option, + /// Whether to show onboarding banners in the title bar. + /// + /// Default: true + pub show_onboarding_banner: Option, + /// Whether to show user avatar in the title bar. + /// + /// Default: true + pub show_user_picture: Option, + /// Whether to show the branch name button in the titlebar. + /// + /// Default: true + pub show_branch_name: Option, + /// Whether to show the project host and name in the titlebar. + /// + /// Default: true + pub show_project_items: Option, + /// Whether to show the sign in button in the title bar. + /// + /// Default: true + pub show_sign_in: Option, + /// Whether to show the menus in the title bar. + /// + /// Default: false + pub show_menus: Option, +} + +#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Debug)] +#[serde(rename_all = "snake_case")] +pub enum TitleBarVisibilityContent { + Always, + Never, + HideInFullScreen, +} + +/// Settings for rendering text in UI and text buffers. +#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] +pub struct ThemeSettingsContent { + /// The default font size for text in the UI. + #[serde(default)] + pub ui_font_size: Option, + /// The name of a font to use for rendering in the UI. + #[serde(default)] + pub ui_font_family: Option, + /// The font fallbacks to use for rendering in the UI. + #[serde(default)] + #[schemars(default = "default_font_fallbacks")] + #[schemars(extend("uniqueItems" = true))] + pub ui_font_fallbacks: Option>, + /// The OpenType features to enable for text in the UI. + #[serde(default)] + #[schemars(default = "default_font_features")] + pub ui_font_features: Option, + /// The weight of the UI font in CSS units from 100 to 900. + #[serde(default)] + pub ui_font_weight: Option, + /// The name of a font to use for rendering in text buffers. + #[serde(default)] + pub buffer_font_family: Option, + /// The font fallbacks to use for rendering in text buffers. + #[serde(default)] + #[schemars(extend("uniqueItems" = true))] + pub buffer_font_fallbacks: Option>, + /// The default font size for rendering in text buffers. + #[serde(default)] + pub buffer_font_size: Option, + /// The weight of the editor font in CSS units from 100 to 900. + #[serde(default)] + pub buffer_font_weight: Option, + /// The buffer's line height. + #[serde(default)] + pub buffer_line_height: Option, + /// The OpenType features to enable for rendering in text buffers. + #[serde(default)] + #[schemars(default = "default_font_features")] + pub buffer_font_features: Option, + /// The font size for the agent panel. Falls back to the UI font size if unset. + #[serde(default)] + pub agent_font_size: Option>, + /// The name of the Zed theme to use. + #[serde(default)] + pub theme: Option, + /// The name of the icon theme to use. + #[serde(default)] + pub icon_theme: Option, + + /// UNSTABLE: Expect many elements to be broken. + /// + // Controls the density of the UI. + #[serde(rename = "unstable.ui_density", default)] + pub ui_density: Option, + + /// How much to fade out unused code. + #[serde(default)] + pub unnecessary_code_fade: Option, + + /// EXPERIMENTAL: Overrides for the current theme. + /// + /// These values will override the ones on the current theme specified in `theme`. + #[serde(rename = "experimental.theme_overrides", default)] + pub experimental_theme_overrides: Option, + + /// Overrides per theme + /// + /// These values will override the ones on the specified theme + #[serde(default)] + pub theme_overrides: HashMap, +} + +fn default_font_features() -> Option { + Some(FontFeatures::default()) +} + +fn default_font_fallbacks() -> Option { + Some(FontFallbacks::default()) +} + +/// Represents the selection of a theme, which can be either static or dynamic. +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(untagged)] +pub enum ThemeSelection { + /// A static theme selection, represented by a single theme name. + Static(ThemeName), + /// A dynamic theme selection, which can change based the [ThemeMode]. + Dynamic { + /// The mode used to determine which theme to use. + #[serde(default)] + mode: ThemeMode, + /// The theme to use for light mode. + light: ThemeName, + /// The theme to use for dark mode. + dark: ThemeName, + }, +} + +/// Represents the selection of an icon theme, which can be either static or dynamic. +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(untagged)] +pub enum IconThemeSelection { + /// A static icon theme selection, represented by a single icon theme name. + Static(IconThemeName), + /// A dynamic icon theme selection, which can change based on the [`ThemeMode`]. + Dynamic { + /// The mode used to determine which theme to use. + #[serde(default)] + mode: ThemeMode, + /// The icon theme to use for light mode. + light: IconThemeName, + /// The icon theme to use for dark mode. + dark: IconThemeName, + }, +} + +// TODO: Rename ThemeMode -> ThemeAppearanceMode +/// The mode use to select a theme. +/// +/// `Light` and `Dark` will select their respective themes. +/// +/// `System` will select the theme based on the system's appearance. +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum ThemeMode { + /// Use the specified `light` theme. + Light, + + /// Use the specified `dark` theme. + Dark, + + /// Use the theme based on the system's appearance. + #[default] + System, +} + +/// Specifies the density of the UI. +/// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078) +#[derive( + Debug, + Default, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Clone, + Copy, + Serialize, + Deserialize, + JsonSchema, +)] +#[serde(rename_all = "snake_case")] +pub enum UiDensity { + /// A denser UI with tighter spacing and smaller elements. + #[serde(alias = "compact")] + Compact, + #[default] + #[serde(alias = "default")] + /// The default UI density. + Default, + #[serde(alias = "comfortable")] + /// A looser UI with more spacing and larger elements. + Comfortable, +} + +impl UiDensity { + /// The spacing ratio of a given density. + /// TODO: Standardize usage throughout the app or remove + pub fn spacing_ratio(self) -> f32 { + match self { + UiDensity::Compact => 0.75, + UiDensity::Default => 1.0, + UiDensity::Comfortable => 1.25, + } + } +} + +/// Newtype for font family name. Its `ParameterizedJsonSchema` lists the font families known at +/// runtime. +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(transparent)] +pub struct FontFamilyName(pub Arc); + +inventory::submit! { + ParameterizedJsonSchema { + add_and_get_ref: |generator, params, _cx| { + replace_subschema::(generator, || { + json_schema!({ + "type": "string", + "enum": params.font_names, + }) + }) + } + } +} + +/// The buffer's line height. +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)] +#[serde(rename_all = "snake_case")] +pub enum BufferLineHeight { + /// A less dense line height. + #[default] + Comfortable, + /// The default line height. + Standard, + /// A custom line height, where 1.0 is the font's height. Must be at least 1.0. + Custom(#[serde(deserialize_with = "deserialize_line_height")] f32), +} + +fn deserialize_line_height<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let value = f32::deserialize(deserializer)?; + if value < 1.0 { + return Err(serde::de::Error::custom( + "buffer_line_height.custom must be at least 1.0", + )); + } + + Ok(value) +} + +/// The content of a serialized theme. +#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq)] +#[serde(default)] +pub struct ThemeStyleContent { + #[serde(default, rename = "background.appearance")] + pub window_background_appearance: Option, + + #[serde(default)] + pub accents: Vec, + + #[serde(flatten, default)] + pub colors: ThemeColorsContent, + + #[serde(flatten, default)] + pub status: StatusColorsContent, + + #[serde(default)] + pub players: Vec, + + /// The styles for syntax nodes. + #[serde(default)] + pub syntax: IndexMap, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)] +pub struct AccentContent(pub Option); + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)] +pub struct PlayerColorContent { + pub cursor: Option, + pub background: Option, + pub selection: Option, +} + +pub(crate) fn try_parse_color(color: &str) -> Result { + let rgba = gpui::Rgba::try_from(color)?; + let rgba = palette::rgb::Srgba::from_components((rgba.r, rgba.g, rgba.b, rgba.a)); + let hsla = palette::Hsla::from_color(rgba); + + let hsla = gpui::hsla( + hsla.hue.into_positive_degrees() / 360., + hsla.saturation, + hsla.lightness, + hsla.alpha, + ); + + Ok(hsla) +} + +/// Newtype for a theme name. Its `ParameterizedJsonSchema` lists the theme names known at runtime. +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(transparent)] +pub struct ThemeName(pub Arc); + +inventory::submit! { + ParameterizedJsonSchema { + add_and_get_ref: |generator, _params, cx| { + todo!() + // replace_subschema::(generator, || json_schema!({ + // "type": "string", + // "enum": ThemeRegistry::global(cx).list_names(), + // })) + } + } +} + +/// Newtype for a icon theme name. Its `ParameterizedJsonSchema` lists the icon theme names known at +/// runtime. +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(transparent)] +pub struct IconThemeName(pub Arc); + +inventory::submit! { + ParameterizedJsonSchema { + add_and_get_ref: |generator, _params, cx| { + todo!() + // replace_subschema::(generator, || json_schema!({ + // "type": "string", + // "enum": ThemeRegistry::global(cx) + // .list_icon_themes() + // .into_iter() + // .map(|icon_theme| icon_theme.name) + // .collect::>(), + // })) + } + } +} diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 5c2f3b8e73a46ba5033919c27a709ad6839136ab..551a3fb31bceb7e924e6685dd51016dae4c00593 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -10,9 +10,8 @@ use futures::{ use gpui::{App, AsyncApp, BorrowAppContext, Global, SharedString, Task, UpdateGlobal}; use paths::{EDITORCONFIG_NAME, local_settings_file_relative_path, task_file_name}; -use schemars::JsonSchema; use serde::{Serialize, de::DeserializeOwned}; -use serde_json::{Value, json}; +use serde_json::Value; use smallvec::SmallVec; use std::{ any::{Any, TypeId, type_name}, @@ -32,7 +31,8 @@ pub type EditorconfigProperties = ec4rs::Properties; use crate::{ ActiveSettingsProfileName, ParameterizedJsonSchema, SettingsJsonSchemaParams, SettingsUiEntry, - VsCodeSettings, WorktreeId, parse_json_with_comments, replace_value_in_json_text, + VsCodeSettings, WorktreeId, default_settings, parse_json_with_comments, + replace_value_in_json_text, settings_content::{ ExtensionsSettingsContent, ProjectSettingsContent, ServerSettingsContent, SettingsContent, UserSettingsContent, @@ -201,7 +201,7 @@ pub struct SettingsLocation<'a> { /// A set of strongly-typed setting values defined via multiple config files. pub struct SettingsStore { setting_values: HashMap>, - default_settings: Option, + default_settings: SettingsContent, user_settings: Option, global_settings: Option, @@ -276,11 +276,12 @@ trait AnySettingValue: 'static + Send + Sync { struct DeserializedSetting(Box); impl SettingsStore { - pub fn new(cx: &App) -> Self { + pub fn new(cx: &App, default_settings: &str) -> Self { let (setting_file_updates_tx, mut setting_file_updates_rx) = mpsc::unbounded(); + let default_settings = parse_json_with_comments(default_settings).unwrap(); Self { setting_values: Default::default(), - default_settings: Some(Default::default()), // todo!() + default_settings, global_settings: None, server_settings: None, user_settings: Some(Default::default()), // todo!() @@ -347,9 +348,8 @@ impl SettingsStore { if let Some(server_settings) = self.server_settings.as_ref() { refinements.push(server_settings) } - let default = self.default_settings.as_ref().unwrap(); // todo!() unwrap... - let mut value = T::from_file(default).unwrap(); + let mut value = T::from_file(&self.default_settings).unwrap(); for refinement in refinements { value.refine(refinement) } @@ -438,17 +438,13 @@ impl SettingsStore { } /// Access the raw JSON value of the default settings. - pub fn raw_default_settings(&self) -> Option<&SettingsContent> { - self.default_settings.as_ref() + pub fn raw_default_settings(&self) -> &SettingsContent { + &self.default_settings } #[cfg(any(test, feature = "test-support"))] pub fn test(cx: &mut App) -> Self { - let mut this = Self::new(cx); - this.set_default_settings(&crate::test_settings(), cx) - .unwrap(); - this.set_user_settings("{}", cx).unwrap(); - this + Self::new(cx, &crate::test_settings()) } /// Updates the value of a setting in the user's global configuration. @@ -715,6 +711,12 @@ impl SettingsStore { parse_json_with_comments(server_settings_content)? }; + // Rewrite the server settings into a content type + self.server_settings = settings.map(|settings| SettingsContent { + project: settings.project, + ..Default::default() + }); + todo!(); // self.server_settings = Some(settings); self.recompute_values(None, cx)?; @@ -1110,13 +1112,12 @@ impl SettingsStore { if let Some(server_settings) = self.server_settings.as_ref() { refinements.push(server_settings) } - let default = self.default_settings.as_ref().unwrap(); for setting_value in self.setting_values.values_mut() { // If the global settings file changed, reload the global value for the field. if changed_local_path.is_none() { - let mut value = setting_value.from_file(&default).unwrap(); - setting_value.refine(&mut value, &refinements); + let mut value = setting_value.from_file(&self.default_settings).unwrap(); + setting_value.refine(value.as_mut(), &refinements); setting_value.set_global_value(value); } @@ -1151,9 +1152,9 @@ impl SettingsStore { continue; } - let mut value = setting_value.from_file(&default).unwrap(); - setting_value.refine(&mut value, &refinements); - setting_value.refine(&mut value, &project_settings_stack); + let mut value = setting_value.from_file(&self.default_settings).unwrap(); + setting_value.refine(value.as_mut(), &refinements); + setting_value.refine(value.as_mut(), &project_settings_stack); setting_value.set_local_value(*root_id, directory_path.clone(), value); } } @@ -1237,10 +1238,12 @@ impl Debug for SettingsStore { impl AnySettingValue for SettingValue { fn from_file(&self, s: &SettingsContent) -> Option> { + dbg!(type_name::(), TypeId::of::()); T::from_file(s).map(|result| Box::new(result) as _) } fn refine(&self, value: &mut dyn Any, refinements: &[&SettingsContent]) { + dbg!(type_name::(), TypeId::of::()); let value = value.downcast_mut::().unwrap(); for refinement in refinements { value.refine(refinement) @@ -1336,7 +1339,10 @@ impl AnySettingValue for SettingValue { #[cfg(test)] mod tests { - use crate::{VsCodeSettingsSource, settings_content::LanguageSettingsContent}; + use crate::{ + TitleBarSettingsContent, TitleBarVisibilityContent, VsCodeSettingsSource, + settings_content::LanguageSettingsContent, test_settings, + }; use super::*; // This is so the SettingsUi macro can still work properly @@ -1344,138 +1350,186 @@ mod tests { use serde::Deserialize; use settings_ui_macros::{SettingsKey, SettingsUi}; use unindent::Unindent; + use util::Refine; - // #[gpui::test] - // fn test_settings_store_basic(cx: &mut App) { - // let mut store = SettingsStore::new(cx); - // store.register_setting::(cx); - // store.register_setting::(cx); - // store.register_setting::(cx); - // store - // .set_default_settings( - // r#"{ - // "turbo": false, - // "user": { - // "name": "John Doe", - // "age": 30, - // "staff": false - // } - // }"#, - // cx, - // ) - // .unwrap(); + #[derive(Debug, PartialEq)] + struct AutoUpdateSetting { + auto_update: bool, + } - // assert_eq!(store.get::(None), &TurboSetting(false)); - // assert_eq!( - // store.get::(None), - // &UserSettings { - // name: "John Doe".to_string(), - // age: 30, - // staff: false, - // } - // ); - // assert_eq!( - // store.get::(None), - // &MultiKeySettings { - // key1: String::new(), - // key2: String::new(), - // } - // ); + impl Settings for AutoUpdateSetting { + fn from_file(content: &SettingsContent) -> Option { + content + .auto_update + .map(|auto_update| AutoUpdateSetting { auto_update }) + } - // store - // .set_user_settings( - // r#"{ - // "turbo": true, - // "user": { "age": 31 }, - // "key1": "a" - // }"#, - // cx, - // ) - // .unwrap(); + fn refine(&mut self, content: &SettingsContent) { + if let Some(auto_update) = content.auto_update { + self.auto_update = auto_update; + } + } - // assert_eq!(store.get::(None), &TurboSetting(true)); - // assert_eq!( - // store.get::(None), - // &UserSettings { - // name: "John Doe".to_string(), - // age: 31, - // staff: false - // } - // ); + fn import_from_vscode(_: &VsCodeSettings, _: &mut SettingsContent) {} + } - // store - // .set_local_settings( - // WorktreeId::from_usize(1), - // Path::new("/root1").into(), - // LocalSettingsKind::Settings, - // Some(r#"{ "user": { "staff": true } }"#), - // cx, - // ) - // .unwrap(); - // store - // .set_local_settings( - // WorktreeId::from_usize(1), - // Path::new("/root1/subdir").into(), - // LocalSettingsKind::Settings, - // Some(r#"{ "user": { "name": "Jane Doe" } }"#), - // cx, - // ) - // .unwrap(); + #[derive(Debug, PartialEq)] + struct TitleBarSettings { + show: TitleBarVisibilityContent, + } - // store - // .set_local_settings( - // WorktreeId::from_usize(1), - // Path::new("/root2").into(), - // LocalSettingsKind::Settings, - // Some(r#"{ "user": { "age": 42 }, "key2": "b" }"#), - // cx, - // ) - // .unwrap(); + impl Settings for TitleBarSettings { + fn from_file(content: &SettingsContent) -> Option { + let content = content.title_bar?; + Some(TitleBarSettings { + show: content.show?, + }) + } - // assert_eq!( - // store.get::(Some(SettingsLocation { - // worktree_id: WorktreeId::from_usize(1), - // path: Path::new("/root1/something"), - // })), - // &UserSettings { - // name: "John Doe".to_string(), - // age: 31, - // staff: true - // } - // ); - // assert_eq!( - // store.get::(Some(SettingsLocation { - // worktree_id: WorktreeId::from_usize(1), - // path: Path::new("/root1/subdir/something") - // })), - // &UserSettings { - // name: "Jane Doe".to_string(), - // age: 31, - // staff: true - // } - // ); - // assert_eq!( - // store.get::(Some(SettingsLocation { - // worktree_id: WorktreeId::from_usize(1), - // path: Path::new("/root2/something") - // })), - // &UserSettings { - // name: "John Doe".to_string(), - // age: 42, - // staff: false - // } - // ); - // assert_eq!( - // store.get::(Some(SettingsLocation { - // worktree_id: WorktreeId::from_usize(1), - // path: Path::new("/root2/something") - // })), - // &MultiKeySettings { - // key1: "a".to_string(), - // key2: "b".to_string(), - // } - // ); - // } + fn refine(&mut self, content: &SettingsContent) { + let Some(content) = content.title_bar else { + return; + }; + self.show.refine(&content.show) + } + + fn import_from_vscode(_: &VsCodeSettings, _: &mut SettingsContent) {} + } + + #[gpui::test] + fn test_settings_store_basic(cx: &mut App) { + let mut store = SettingsStore::new( + cx, + r#"{ + "auto_update": false, + "user": { + "name": "John Doe", + "age": 30, + "staff": false + } + }"#, + ); + store.register_setting::(cx); + store.register_setting::(cx); + // store.register_setting::(cx); + + assert_eq!( + store.get::(None), + &AutoUpdateSetting { auto_update: false } + ); + // assert_eq!( + // store.get::(None), + // &UserSettings { + // name: "John Doe".to_string(), + // age: 30, + // staff: false, + // } + // ); + // assert_eq!( + // store.get::(None), + // &MultiKeySettings { + // key1: String::new(), + // key2: String::new(), + // } + // ); + + store + .set_user_settings( + r#"{ + "auto_update": true, + "user": { "age": 31 }, + "key1": "a" + }"#, + cx, + ) + .unwrap(); + + assert_eq!( + store.get::(None), + &AutoUpdateSetting { auto_update: true } + ); + // assert_eq!( + // store.get::(None), + // &UserSettings { + // name: "John Doe".to_string(), + // age: 31, + // staff: false + // } + // ); + + store + .set_local_settings( + WorktreeId::from_usize(1), + Path::new("/root1").into(), + LocalSettingsKind::Settings, + Some(r#"{ "user": { "staff": true } }"#), + cx, + ) + .unwrap(); + store + .set_local_settings( + WorktreeId::from_usize(1), + Path::new("/root1/subdir").into(), + LocalSettingsKind::Settings, + Some(r#"{ "user": { "name": "Jane Doe" } }"#), + cx, + ) + .unwrap(); + + store + .set_local_settings( + WorktreeId::from_usize(1), + Path::new("/root2").into(), + LocalSettingsKind::Settings, + Some(r#"{ "user": { "age": 42 }, "key2": "b" }"#), + cx, + ) + .unwrap(); + + // assert_eq!( + // store.get::(Some(SettingsLocation { + // worktree_id: WorktreeId::from_usize(1), + // path: Path::new("/root1/something"), + // })), + // &UserSettings { + // name: "John Doe".to_string(), + // age: 31, + // staff: true + // } + // ); + // assert_eq!( + // store.get::(Some(SettingsLocation { + // worktree_id: WorktreeId::from_usize(1), + // path: Path::new("/root1/subdir/something") + // })), + // &UserSettings { + // name: "Jane Doe".to_string(), + // age: 31, + // staff: true + // } + // ); + // assert_eq!( + // store.get::(Some(SettingsLocation { + // worktree_id: WorktreeId::from_usize(1), + // path: Path::new("/root2/something") + // })), + // &UserSettings { + // name: "John Doe".to_string(), + // age: 42, + // staff: false + // } + // ); + // assert_eq!( + // store.get::(Some(SettingsLocation { + // worktree_id: WorktreeId::from_usize(1), + // path: Path::new("/root2/something") + // })), + // &MultiKeySettings { + // key1: "a".to_string(), + // key2: "b".to_string(), + // } + // ); + } // #[gpui::test] // fn test_setting_store_assign_json_before_register(cx: &mut App) { @@ -1538,7 +1592,7 @@ mod tests { #[gpui::test] fn test_setting_store_update(cx: &mut App) { - let mut store = SettingsStore::new(cx); + let mut store = SettingsStore::new(cx, &test_settings()); // store.register_setting::(cx); // store.register_setting::(cx); // store.register_setting::(cx); diff --git a/crates/theme/src/settings.rs b/crates/theme/src/settings.rs index 8409c60b22b03b8d917b84ae20229dc2db63fe4a..500ab2c03cf7ee01b245ca2bc89bd2c218f66d1a 100644 --- a/crates/theme/src/settings.rs +++ b/crates/theme/src/settings.rs @@ -819,8 +819,6 @@ fn clamp_font_weight(weight: f32) -> FontWeight { } impl settings::Settings for ThemeSettings { - type FileContent = ThemeSettingsContent; - fn load(sources: SettingsSources, cx: &mut App) -> Result { let themes = ThemeRegistry::default_global(cx); let system_appearance = SystemAppearance::default_global(cx); diff --git a/crates/title_bar/src/title_bar_settings.rs b/crates/title_bar/src/title_bar_settings.rs index 240c1fd74f0b6f49feef09ce20a81e5f54adfcb1..1af7c4547493e704c002bb08a2e4a862bd192931 100644 --- a/crates/title_bar/src/title_bar_settings.rs +++ b/crates/title_bar/src/title_bar_settings.rs @@ -1,7 +1,7 @@ use db::anyhow; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsKey, SettingsSources, SettingsUi}; +use settings::{Settings, SettingsContent, SettingsKey, SettingsSources, SettingsUi}; #[derive(Copy, Clone, Serialize, Deserialize, JsonSchema, Debug, SettingsUi)] #[serde(rename_all = "snake_case")] @@ -11,7 +11,7 @@ pub enum TitleBarVisibility { HideInFullScreen, } -#[derive(Copy, Clone, Deserialize, Debug)] +#[derive(Copy, Clone, Debug)] pub struct TitleBarSettings { pub show: TitleBarVisibility, pub show_branch_icon: bool, @@ -23,54 +23,34 @@ pub struct TitleBarSettings { pub show_menus: bool, } -#[derive( - Copy, Clone, Default, Serialize, Deserialize, JsonSchema, Debug, SettingsUi, SettingsKey, -)] -#[settings_ui(group = "Title Bar")] -#[settings_key(key = "title_bar")] -pub struct TitleBarSettingsContent { - /// Controls when the title bar is visible: "always" | "never" | "hide_in_full_screen". - /// - /// Default: "always" - pub show: Option, - /// Whether to show the branch icon beside branch switcher in the title bar. - /// - /// Default: false - pub show_branch_icon: Option, - /// Whether to show onboarding banners in the title bar. - /// - /// Default: true - pub show_onboarding_banner: Option, - /// Whether to show user avatar in the title bar. - /// - /// Default: true - pub show_user_picture: Option, - /// Whether to show the branch name button in the titlebar. - /// - /// Default: true - pub show_branch_name: Option, - /// Whether to show the project host and name in the titlebar. - /// - /// Default: true - pub show_project_items: Option, - /// Whether to show the sign in button in the title bar. - /// - /// Default: true - pub show_sign_in: Option, - /// Whether to show the menus in the title bar. - /// - /// Default: false - pub show_menus: Option, -} - impl Settings for TitleBarSettings { - type FileContent = TitleBarSettingsContent; + fn from_file(s: &SettingsContent) -> Option { + let content = s.title_bar?; + TitleBarSettings { + show: content.show?, + show_branch_icon: content.show_branch_icon?, + show_onboarding_banner: content.show_onboarding_banner?, + show_user_picture: content.show_user_picture?, + show_branch_name: content.show_branch_name?, + show_project_items: content.show_project_items?, + show_sign_in: content.show_sign_in?, + show_menus: content.show_menus?, + } + } + + fn refine(&mut self, s: &SettingsContent) { + let Some(content) = s.title_bar else { + return + } - fn load(sources: SettingsSources, _: &mut gpui::App) -> anyhow::Result - where - Self: Sized, - { - sources.json_merge() + self.show.refine(&content.show); + self.show_branch_icon.refine(content.show_branch_icon); + self.show_onboarding_banner.refine(content.show_onboarding_banner); + self.show_user_picture.refine(content.show_user_picture); + self.show_branch_name.refine(content.show_branch_name); + self.show_project_items.refine(content.show_project_items); + self.show_sign_in.refine(content.show_sign_in); + self.show_menus.refine(content.show_menus); } fn import_from_vscode(_: &settings::VsCodeSettings, _: &mut Self::FileContent) {} diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index 90f5be1c92875ac0b9b2d3e7352ae858371b3686..986b0c8ac822fe0ebb981a6bd0d168f915109f0a 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -1381,3 +1381,21 @@ Line 3"# assert_eq!(result[1], (10..15, "world")); // '🦀' is 4 bytes } } + +pub fn refine(dest: &mut T, src: &Option) { + if let Some(src) = src { + *dest = src.clone() + } +} + +pub trait Refine: Sized + Clone { + fn refine(&mut self, src: &Option); +} + +impl Refine for T { + fn refine(&mut self, src: &Option) { + if let Some(src) = src { + *self = src.clone(); + } + } +} diff --git a/crates/zeta_cli/src/headless.rs b/crates/zeta_cli/src/headless.rs index a11fd707591d0403409b5bc9e243d35c70fd65d3..04d4e9279a5a6a82081078f338e215ca67dca3e8 100644 --- a/crates/zeta_cli/src/headless.rs +++ b/crates/zeta_cli/src/headless.rs @@ -31,10 +31,7 @@ pub fn init(cx: &mut App) -> ZetaCliAppState { release_channel::init(app_version, cx); gpui_tokio::init(cx); - let mut settings_store = SettingsStore::new(cx); - settings_store - .set_default_settings(settings::default_settings().as_ref(), cx) - .unwrap(); + let mut settings_store = SettingsStore::new(cx, settings::default_settings()); cx.set_global(settings_store); client::init_settings(cx);