Detailed changes
@@ -340,10 +340,7 @@ pub fn init(cx: &mut App) -> Arc<AgentAppState> {
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);
@@ -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();
@@ -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<BaseKeymapContent>,
+
+ pub auto_update: Option<bool>,
+
+ pub title_bar: Option<TitleBarSettingsContent>,
}
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<TitleBarVisibilityContent>,
+ /// Whether to show the branch icon beside branch switcher in the title bar.
+ ///
+ /// Default: false
+ pub show_branch_icon: Option<bool>,
+ /// Whether to show onboarding banners in the title bar.
+ ///
+ /// Default: true
+ pub show_onboarding_banner: Option<bool>,
+ /// Whether to show user avatar in the title bar.
+ ///
+ /// Default: true
+ pub show_user_picture: Option<bool>,
+ /// Whether to show the branch name button in the titlebar.
+ ///
+ /// Default: true
+ pub show_branch_name: Option<bool>,
+ /// Whether to show the project host and name in the titlebar.
+ ///
+ /// Default: true
+ pub show_project_items: Option<bool>,
+ /// Whether to show the sign in button in the title bar.
+ ///
+ /// Default: true
+ pub show_sign_in: Option<bool>,
+ /// Whether to show the menus in the title bar.
+ ///
+ /// Default: false
+ pub show_menus: Option<bool>,
+}
+
+#[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<f32>,
+ /// The name of a font to use for rendering in the UI.
+ #[serde(default)]
+ pub ui_font_family: Option<FontFamilyName>,
+ /// 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<Vec<FontFamilyName>>,
+ /// The OpenType features to enable for text in the UI.
+ #[serde(default)]
+ #[schemars(default = "default_font_features")]
+ pub ui_font_features: Option<FontFeatures>,
+ /// The weight of the UI font in CSS units from 100 to 900.
+ #[serde(default)]
+ pub ui_font_weight: Option<f32>,
+ /// The name of a font to use for rendering in text buffers.
+ #[serde(default)]
+ pub buffer_font_family: Option<FontFamilyName>,
+ /// The font fallbacks to use for rendering in text buffers.
+ #[serde(default)]
+ #[schemars(extend("uniqueItems" = true))]
+ pub buffer_font_fallbacks: Option<Vec<FontFamilyName>>,
+ /// The default font size for rendering in text buffers.
+ #[serde(default)]
+ pub buffer_font_size: Option<f32>,
+ /// The weight of the editor font in CSS units from 100 to 900.
+ #[serde(default)]
+ pub buffer_font_weight: Option<f32>,
+ /// The buffer's line height.
+ #[serde(default)]
+ pub buffer_line_height: Option<BufferLineHeight>,
+ /// The OpenType features to enable for rendering in text buffers.
+ #[serde(default)]
+ #[schemars(default = "default_font_features")]
+ pub buffer_font_features: Option<FontFeatures>,
+ /// The font size for the agent panel. Falls back to the UI font size if unset.
+ #[serde(default)]
+ pub agent_font_size: Option<Option<f32>>,
+ /// The name of the Zed theme to use.
+ #[serde(default)]
+ pub theme: Option<ThemeSelection>,
+ /// The name of the icon theme to use.
+ #[serde(default)]
+ pub icon_theme: Option<IconThemeSelection>,
+
+ /// UNSTABLE: Expect many elements to be broken.
+ ///
+ // Controls the density of the UI.
+ #[serde(rename = "unstable.ui_density", default)]
+ pub ui_density: Option<UiDensity>,
+
+ /// How much to fade out unused code.
+ #[serde(default)]
+ pub unnecessary_code_fade: Option<f32>,
+
+ /// 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<ThemeStyleContent>,
+
+ /// Overrides per theme
+ ///
+ /// These values will override the ones on the specified theme
+ #[serde(default)]
+ pub theme_overrides: HashMap<String, ThemeStyleContent>,
+}
+
+fn default_font_features() -> Option<FontFeatures> {
+ Some(FontFeatures::default())
+}
+
+fn default_font_fallbacks() -> Option<FontFallbacks> {
+ 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<str>);
+
+inventory::submit! {
+ ParameterizedJsonSchema {
+ add_and_get_ref: |generator, params, _cx| {
+ replace_subschema::<FontFamilyName>(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<f32, D::Error>
+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<WindowBackgroundContent>,
+
+ #[serde(default)]
+ pub accents: Vec<AccentContent>,
+
+ #[serde(flatten, default)]
+ pub colors: ThemeColorsContent,
+
+ #[serde(flatten, default)]
+ pub status: StatusColorsContent,
+
+ #[serde(default)]
+ pub players: Vec<PlayerColorContent>,
+
+ /// The styles for syntax nodes.
+ #[serde(default)]
+ pub syntax: IndexMap<String, HighlightStyleContent>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
+pub struct AccentContent(pub Option<String>);
+
+#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
+pub struct PlayerColorContent {
+ pub cursor: Option<String>,
+ pub background: Option<String>,
+ pub selection: Option<String>,
+}
+
+pub(crate) fn try_parse_color(color: &str) -> Result<Hsla> {
+ 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<str>);
+
+inventory::submit! {
+ ParameterizedJsonSchema {
+ add_and_get_ref: |generator, _params, cx| {
+ todo!()
+ // replace_subschema::<ThemeName>(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<str>);
+
+inventory::submit! {
+ ParameterizedJsonSchema {
+ add_and_get_ref: |generator, _params, cx| {
+ todo!()
+ // replace_subschema::<IconThemeName>(generator, || json_schema!({
+ // "type": "string",
+ // "enum": ThemeRegistry::global(cx)
+ // .list_icon_themes()
+ // .into_iter()
+ // .map(|icon_theme| icon_theme.name)
+ // .collect::<Vec<SharedString>>(),
+ // }))
+ }
+ }
+}
@@ -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<TypeId, Box<dyn AnySettingValue>>,
- default_settings: Option<SettingsContent>,
+ default_settings: SettingsContent,
user_settings: Option<UserSettingsContent>,
global_settings: Option<SettingsContent>,
@@ -276,11 +276,12 @@ trait AnySettingValue: 'static + Send + Sync {
struct DeserializedSetting(Box<dyn Any>);
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<T: Settings> AnySettingValue for SettingValue<T> {
fn from_file(&self, s: &SettingsContent) -> Option<Box<dyn Any>> {
+ dbg!(type_name::<T>(), TypeId::of::<T>());
T::from_file(s).map(|result| Box::new(result) as _)
}
fn refine(&self, value: &mut dyn Any, refinements: &[&SettingsContent]) {
+ dbg!(type_name::<T>(), TypeId::of::<T>());
let value = value.downcast_mut::<T>().unwrap();
for refinement in refinements {
value.refine(refinement)
@@ -1336,7 +1339,10 @@ impl<T: Settings> AnySettingValue for SettingValue<T> {
#[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::<UserSettings>(cx);
- // store.register_setting::<TurboSetting>(cx);
- // store.register_setting::<MultiKeySettings>(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::<TurboSetting>(None), &TurboSetting(false));
- // assert_eq!(
- // store.get::<UserSettings>(None),
- // &UserSettings {
- // name: "John Doe".to_string(),
- // age: 30,
- // staff: false,
- // }
- // );
- // assert_eq!(
- // store.get::<MultiKeySettings>(None),
- // &MultiKeySettings {
- // key1: String::new(),
- // key2: String::new(),
- // }
- // );
+ impl Settings for AutoUpdateSetting {
+ fn from_file(content: &SettingsContent) -> Option<Self> {
+ 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::<TurboSetting>(None), &TurboSetting(true));
- // assert_eq!(
- // store.get::<UserSettings>(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<Self> {
+ let content = content.title_bar?;
+ Some(TitleBarSettings {
+ show: content.show?,
+ })
+ }
- // assert_eq!(
- // store.get::<UserSettings>(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::<UserSettings>(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::<UserSettings>(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::<MultiKeySettings>(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::<AutoUpdateSetting>(cx);
+ store.register_setting::<TitleBarSettings>(cx);
+ // store.register_setting::<MultiKeySettings>(cx);
+
+ assert_eq!(
+ store.get::<AutoUpdateSetting>(None),
+ &AutoUpdateSetting { auto_update: false }
+ );
+ // assert_eq!(
+ // store.get::<UserSettings>(None),
+ // &UserSettings {
+ // name: "John Doe".to_string(),
+ // age: 30,
+ // staff: false,
+ // }
+ // );
+ // assert_eq!(
+ // store.get::<MultiKeySettings>(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::<AutoUpdateSetting>(None),
+ &AutoUpdateSetting { auto_update: true }
+ );
+ // assert_eq!(
+ // store.get::<UserSettings>(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::<UserSettings>(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::<UserSettings>(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::<UserSettings>(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::<MultiKeySettings>(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::<MultiKeySettings>(cx);
// store.register_setting::<UserSettings>(cx);
// store.register_setting::<LanguageSettings>(cx);
@@ -819,8 +819,6 @@ fn clamp_font_weight(weight: f32) -> FontWeight {
}
impl settings::Settings for ThemeSettings {
- type FileContent = ThemeSettingsContent;
-
fn load(sources: SettingsSources<Self::FileContent>, cx: &mut App) -> Result<Self> {
let themes = ThemeRegistry::default_global(cx);
let system_appearance = SystemAppearance::default_global(cx);
@@ -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<TitleBarVisibility>,
- /// Whether to show the branch icon beside branch switcher in the title bar.
- ///
- /// Default: false
- pub show_branch_icon: Option<bool>,
- /// Whether to show onboarding banners in the title bar.
- ///
- /// Default: true
- pub show_onboarding_banner: Option<bool>,
- /// Whether to show user avatar in the title bar.
- ///
- /// Default: true
- pub show_user_picture: Option<bool>,
- /// Whether to show the branch name button in the titlebar.
- ///
- /// Default: true
- pub show_branch_name: Option<bool>,
- /// Whether to show the project host and name in the titlebar.
- ///
- /// Default: true
- pub show_project_items: Option<bool>,
- /// Whether to show the sign in button in the title bar.
- ///
- /// Default: true
- pub show_sign_in: Option<bool>,
- /// Whether to show the menus in the title bar.
- ///
- /// Default: false
- pub show_menus: Option<bool>,
-}
-
impl Settings for TitleBarSettings {
- type FileContent = TitleBarSettingsContent;
+ fn from_file(s: &SettingsContent) -> Option<Self> {
+ 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<Self::FileContent>, _: &mut gpui::App) -> anyhow::Result<Self>
- 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) {}
@@ -1381,3 +1381,21 @@ Line 3"#
assert_eq!(result[1], (10..15, "world")); // '🦀' is 4 bytes
}
}
+
+pub fn refine<T: Clone>(dest: &mut T, src: &Option<T>) {
+ if let Some(src) = src {
+ *dest = src.clone()
+ }
+}
+
+pub trait Refine: Sized + Clone {
+ fn refine(&mut self, src: &Option<Self>);
+}
+
+impl<T: Clone> Refine for T {
+ fn refine(&mut self, src: &Option<Self>) {
+ if let Some(src) = src {
+ *self = src.clone();
+ }
+ }
+}
@@ -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);