Revert "Fix settings2 crate tests"

Conrad Irwin created

This reverts commit 6e9c779c5156573e5dcc80f26b5a77a658e1eaaa.

Change summary

Cargo.lock                             |   3 
crates/language2/Cargo.toml            |   2 
crates/settings2/Cargo.toml            |   2 
crates/settings2/src/settings_file.rs  |   2 
crates/settings2/src/settings_store.rs | 909 ++++++++++++++-------------
crates/theme2/src/settings.rs          |   2 
crates/theme2/src/theme2.rs            |   1 
7 files changed, 475 insertions(+), 446 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -4248,7 +4248,7 @@ dependencies = [
  "collections",
  "ctor",
  "env_logger 0.9.3",
- "fs2",
+ "fs",
  "futures 0.3.28",
  "fuzzy2",
  "git",
@@ -7515,7 +7515,6 @@ dependencies = [
  "collections",
  "feature_flags2",
  "fs",
- "fs2",
  "futures 0.3.28",
  "gpui2",
  "indoc",

crates/language2/Cargo.toml 🔗

@@ -25,7 +25,7 @@ test-support = [
 clock = { path = "../clock" }
 collections = { path = "../collections" }
 fuzzy2 = { path = "../fuzzy2" }
-fs2 = { path = "../fs2" }
+fs = { path = "../fs" }
 git = { path = "../git" }
 gpui2 = { path = "../gpui2" }
 lsp2 = { path = "../lsp2" }

crates/settings2/Cargo.toml 🔗

@@ -15,7 +15,7 @@ test-support = ["gpui2/test-support", "fs/test-support"]
 collections = { path = "../collections" }
 gpui2 = { path = "../gpui2" }
 sqlez = { path = "../sqlez" }
-fs2 = { path = "../fs2" }
+fs = { path = "../fs" }
 feature_flags2 = { path = "../feature_flags2" }
 util = { path = "../util" }
 

crates/settings2/src/settings_file.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{settings_store::SettingsStore, Settings};
 use anyhow::Result;
-use fs2::Fs;
+use fs::Fs;
 use futures::{channel::mpsc, StreamExt};
 use gpui2::{AppContext, Executor};
 use std::{io::ErrorKind, path::PathBuf, str, sync::Arc, time::Duration};

crates/settings2/src/settings_store.rs 🔗

@@ -35,7 +35,7 @@ pub trait Settings: 'static + Send + Sync {
     fn load(
         default_value: &Self::FileContent,
         user_values: &[&Self::FileContent],
-        cx: &AppContext,
+        cx: &mut AppContext,
     ) -> Result<Self>
     where
         Self: Sized;
@@ -76,6 +76,36 @@ pub trait Settings: 'static + Send + Sync {
     fn missing_default() -> anyhow::Error {
         anyhow::anyhow!("missing default")
     }
+
+    fn register(cx: &mut AppContext)
+    where
+        Self: Sized,
+    {
+        cx.update_global(|store: &mut SettingsStore, cx| {
+            store.register_setting::<Self>(cx);
+        });
+    }
+
+    fn get<'a>(path: Option<(usize, &Path)>, cx: &'a AppContext) -> &'a Self
+    where
+        Self: Sized,
+    {
+        cx.global::<SettingsStore>().get(path)
+    }
+
+    fn get_global<'a>(cx: &'a AppContext) -> &'a Self
+    where
+        Self: Sized,
+    {
+        cx.global::<SettingsStore>().get(None)
+    }
+
+    fn override_global<'a>(settings: Self, cx: &'a mut AppContext)
+    where
+        Self: Sized,
+    {
+        cx.global_mut::<SettingsStore>().override_global(settings)
+    }
 }
 
 pub struct SettingsJsonSchemaParams<'a> {
@@ -121,7 +151,7 @@ trait AnySettingValue: 'static + Send + Sync {
         &self,
         default_value: &DeserializedSetting,
         custom: &[DeserializedSetting],
-        cx: &AppContext,
+        cx: &mut AppContext,
     ) -> Result<Box<dyn Any>>;
     fn value_for_path(&self, path: Option<(usize, &Path)>) -> &dyn Any;
     fn set_global_value(&mut self, value: Box<dyn Any>);
@@ -138,7 +168,7 @@ struct DeserializedSetting(Box<dyn Any>);
 
 impl SettingsStore {
     /// Add a new type of setting to the store.
-    pub fn register_setting<T: Settings>(&mut self, cx: &AppContext) {
+    pub fn register_setting<T: Settings>(&mut self, cx: &mut AppContext) {
         let setting_type_id = TypeId::of::<T>();
         let entry = self.setting_values.entry(setting_type_id);
         if matches!(entry, hash_map::Entry::Occupied(_)) {
@@ -205,7 +235,7 @@ impl SettingsStore {
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn test(cx: &AppContext) -> Self {
+    pub fn test(cx: &mut AppContext) -> Self {
         let mut this = Self::default();
         this.set_default_settings(&crate::test_settings(), cx)
             .unwrap();
@@ -220,7 +250,7 @@ impl SettingsStore {
     #[cfg(any(test, feature = "test-support"))]
     pub fn update_user_settings<T: Settings>(
         &mut self,
-        cx: &AppContext,
+        cx: &mut AppContext,
         update: impl FnOnce(&mut T::FileContent),
     ) {
         let old_text = serde_json::to_string(&self.raw_user_settings).unwrap();
@@ -317,7 +347,7 @@ impl SettingsStore {
     pub fn set_default_settings(
         &mut self,
         default_settings_content: &str,
-        cx: &AppContext,
+        cx: &mut AppContext,
     ) -> Result<()> {
         let settings: serde_json::Value = parse_json_with_comments(default_settings_content)?;
         if settings.is_object() {
@@ -333,7 +363,7 @@ impl SettingsStore {
     pub fn set_user_settings(
         &mut self,
         user_settings_content: &str,
-        cx: &AppContext,
+        cx: &mut AppContext,
     ) -> Result<()> {
         let settings: serde_json::Value = parse_json_with_comments(user_settings_content)?;
         if settings.is_object() {
@@ -351,7 +381,7 @@ impl SettingsStore {
         root_id: usize,
         path: Arc<Path>,
         settings_content: Option<&str>,
-        cx: &AppContext,
+        cx: &mut AppContext,
     ) -> Result<()> {
         if let Some(content) = settings_content {
             self.raw_local_settings
@@ -364,7 +394,7 @@ impl SettingsStore {
     }
 
     /// Add or remove a set of local settings via a JSON string.
-    pub fn clear_local_settings(&mut self, root_id: usize, cx: &AppContext) -> Result<()> {
+    pub fn clear_local_settings(&mut self, root_id: usize, cx: &mut AppContext) -> Result<()> {
         self.raw_local_settings.retain(|k, _| k.0 != root_id);
         self.recompute_values(Some((root_id, "".as_ref())), cx)?;
         Ok(())
@@ -456,7 +486,7 @@ impl SettingsStore {
     fn recompute_values(
         &mut self,
         changed_local_path: Option<(usize, &Path)>,
-        cx: &AppContext,
+        cx: &mut AppContext,
     ) -> Result<()> {
         // Reload the global and local values for every setting.
         let mut user_settings_stack = Vec::<DeserializedSetting>::new();
@@ -557,7 +587,7 @@ impl<T: Settings> AnySettingValue for SettingValue<T> {
         &self,
         default_value: &DeserializedSetting,
         user_values: &[DeserializedSetting],
-        cx: &AppContext,
+        cx: &mut AppContext,
     ) -> Result<Box<dyn Any>> {
         let default_value = default_value.0.downcast_ref::<T::FileContent>().unwrap();
         let values: SmallVec<[&T::FileContent; 6]> = user_values
@@ -841,432 +871,431 @@ pub fn parse_json_with_comments<T: DeserializeOwned>(content: &str) -> Result<T>
     Ok(serde_json_lenient::from_str(content)?)
 }
 
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use serde_derive::Deserialize;
-    use unindent::Unindent;
-
-    #[gpui2::test]
-    fn test_settings_store_basic(cx: &mut AppContext) {
-        let mut store = SettingsStore::default();
-        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();
-
-        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(),
-            }
-        );
-
-        store
-            .set_user_settings(
-                r#"{
-                 "turbo": true,
-                 "user": { "age": 31 },
-                 "key1": "a"
-                 }"#,
-                cx,
-            )
-            .unwrap();
-
-        assert_eq!(store.get::<TurboSetting>(None), &TurboSetting(true));
-        assert_eq!(
-            store.get::<UserSettings>(None),
-            &UserSettings {
-                name: "John Doe".to_string(),
-                age: 31,
-                staff: false
-            }
-        );
-
-        store
-            .set_local_settings(
-                1,
-                Path::new("/root1").into(),
-                Some(r#"{ "user": { "staff": true } }"#),
-                cx,
-            )
-            .unwrap();
-        store
-            .set_local_settings(
-                1,
-                Path::new("/root1/subdir").into(),
-                Some(r#"{ "user": { "name": "Jane Doe" } }"#),
-                cx,
-            )
-            .unwrap();
-
-        store
-            .set_local_settings(
-                1,
-                Path::new("/root2").into(),
-                Some(r#"{ "user": { "age": 42 }, "key2": "b" }"#),
-                cx,
-            )
-            .unwrap();
-
-        assert_eq!(
-            store.get::<UserSettings>(Some((1, Path::new("/root1/something")))),
-            &UserSettings {
-                name: "John Doe".to_string(),
-                age: 31,
-                staff: true
-            }
-        );
-        assert_eq!(
-            store.get::<UserSettings>(Some((1, Path::new("/root1/subdir/something")))),
-            &UserSettings {
-                name: "Jane Doe".to_string(),
-                age: 31,
-                staff: true
-            }
-        );
-        assert_eq!(
-            store.get::<UserSettings>(Some((1, Path::new("/root2/something")))),
-            &UserSettings {
-                name: "John Doe".to_string(),
-                age: 42,
-                staff: false
-            }
-        );
-        assert_eq!(
-            store.get::<MultiKeySettings>(Some((1, Path::new("/root2/something")))),
-            &MultiKeySettings {
-                key1: "a".to_string(),
-                key2: "b".to_string(),
-            }
-        );
-    }
-
-    #[gpui2::test]
-    fn test_setting_store_assign_json_before_register(cx: &mut AppContext) {
-        let mut store = SettingsStore::default();
-        store
-            .set_default_settings(
-                r#"{
-                     "turbo": true,
-                     "user": {
-                     "name": "John Doe",
-                     "age": 30,
-                     "staff": false
-                     },
-                     "key1": "x"
-                     }"#,
-                cx,
-            )
-            .unwrap();
-        store
-            .set_user_settings(r#"{ "turbo": false }"#, cx)
-            .unwrap();
-        store.register_setting::<UserSettings>(cx);
-        store.register_setting::<TurboSetting>(cx);
-
-        assert_eq!(store.get::<TurboSetting>(None), &TurboSetting(false));
-        assert_eq!(
-            store.get::<UserSettings>(None),
-            &UserSettings {
-                name: "John Doe".to_string(),
-                age: 30,
-                staff: false,
-            }
-        );
-
-        store.register_setting::<MultiKeySettings>(cx);
-        assert_eq!(
-            store.get::<MultiKeySettings>(None),
-            &MultiKeySettings {
-                key1: "x".into(),
-                key2: String::new(),
-            }
-        );
-    }
-
-    #[gpui2::test]
-    fn test_setting_store_update(cx: &mut AppContext) {
-        let mut store = SettingsStore::default();
-        store.register_setting::<MultiKeySettings>(cx);
-        store.register_setting::<UserSettings>(cx);
-        store.register_setting::<LanguageSettings>(cx);
-
-        // entries added and updated
-        check_settings_update::<LanguageSettings>(
-            &mut store,
-            r#"{
-                "languages": {
-                    "JSON": {
-                        "language_setting_1": true
-                    }
-                }
-            }"#
-            .unindent(),
-            |settings| {
-                settings
-                    .languages
-                    .get_mut("JSON")
-                    .unwrap()
-                    .language_setting_1 = Some(false);
-                settings.languages.insert(
-                    "Rust".into(),
-                    LanguageSettingEntry {
-                        language_setting_2: Some(true),
-                        ..Default::default()
-                    },
-                );
-            },
-            r#"{
-                "languages": {
-                    "Rust": {
-                        "language_setting_2": true
-                    },
-                    "JSON": {
-                        "language_setting_1": false
-                    }
-                }
-            }"#
-            .unindent(),
-            cx,
-        );
-
-        // weird formatting
-        check_settings_update::<UserSettings>(
-            &mut store,
-            r#"{
-                "user":   { "age": 36, "name": "Max", "staff": true }
-            }"#
-            .unindent(),
-            |settings| settings.age = Some(37),
-            r#"{
-                "user":   { "age": 37, "name": "Max", "staff": true }
-            }"#
-            .unindent(),
-            cx,
-        );
-
-        // single-line formatting, other keys
-        check_settings_update::<MultiKeySettings>(
-            &mut store,
-            r#"{ "one": 1, "two": 2 }"#.unindent(),
-            |settings| settings.key1 = Some("x".into()),
-            r#"{ "key1": "x", "one": 1, "two": 2 }"#.unindent(),
-            cx,
-        );
-
-        // empty object
-        check_settings_update::<UserSettings>(
-            &mut store,
-            r#"{
-                "user": {}
-            }"#
-            .unindent(),
-            |settings| settings.age = Some(37),
-            r#"{
-                "user": {
-                    "age": 37
-                }
-            }"#
-            .unindent(),
-            cx,
-        );
-
-        // no content
-        check_settings_update::<UserSettings>(
-            &mut store,
-            r#""#.unindent(),
-            |settings| settings.age = Some(37),
-            r#"{
-                "user": {
-                    "age": 37
-                }
-            }
-            "#
-            .unindent(),
-            cx,
-        );
-
-        check_settings_update::<UserSettings>(
-            &mut store,
-            r#"{
-                     }
-                     "#
-            .unindent(),
-            |settings| settings.age = Some(37),
-            r#"{
-                "user": {
-                    "age": 37
-                }
-            }
-            "#
-            .unindent(),
-            cx,
-        );
-    }
-
-    #[track_caller]
-    fn check_settings_update<T: Settings>(
-        store: &mut SettingsStore,
-        old_json: String,
-        update: fn(&mut T::FileContent),
-        expected_new_json: String,
-        cx: &mut AppContext,
-    ) {
-        store.set_user_settings(&old_json, cx).ok();
-        let edits = store.edits_for_update::<T>(&old_json, update);
-        let mut new_json = old_json;
-        for (range, replacement) in edits.into_iter() {
-            new_json.replace_range(range, &replacement);
-        }
-        pretty_assertions::assert_eq!(new_json, expected_new_json);
-    }
-
-    #[derive(Debug, PartialEq, Deserialize)]
-    struct UserSettings {
-        name: String,
-        age: u32,
-        staff: bool,
-    }
-
-    #[derive(Default, Clone, Serialize, Deserialize, JsonSchema)]
-    struct UserSettingsJson {
-        name: Option<String>,
-        age: Option<u32>,
-        staff: Option<bool>,
-    }
-
-    impl Settings for UserSettings {
-        const KEY: Option<&'static str> = Some("user");
-        type FileContent = UserSettingsJson;
-
-        fn load(
-            default_value: &UserSettingsJson,
-            user_values: &[&UserSettingsJson],
-            _: &AppContext,
-        ) -> Result<Self> {
-            Self::load_via_json_merge(default_value, user_values)
-        }
-    }
-
-    #[derive(Debug, Deserialize, PartialEq)]
-    struct TurboSetting(bool);
-
-    impl Settings for TurboSetting {
-        const KEY: Option<&'static str> = Some("turbo");
-        type FileContent = Option<bool>;
-
-        fn load(
-            default_value: &Option<bool>,
-            user_values: &[&Option<bool>],
-            _: &AppContext,
-        ) -> Result<Self> {
-            Self::load_via_json_merge(default_value, user_values)
-        }
-    }
-
-    #[derive(Clone, Debug, PartialEq, Deserialize)]
-    struct MultiKeySettings {
-        #[serde(default)]
-        key1: String,
-        #[serde(default)]
-        key2: String,
-    }
-
-    #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
-    struct MultiKeySettingsJson {
-        key1: Option<String>,
-        key2: Option<String>,
-    }
-
-    impl Settings for MultiKeySettings {
-        const KEY: Option<&'static str> = None;
-
-        type FileContent = MultiKeySettingsJson;
-
-        fn load(
-            default_value: &MultiKeySettingsJson,
-            user_values: &[&MultiKeySettingsJson],
-            _: &AppContext,
-        ) -> Result<Self> {
-            Self::load_via_json_merge(default_value, user_values)
-        }
-    }
-
-    #[derive(Debug, Deserialize)]
-    struct JournalSettings {
-        pub path: String,
-        pub hour_format: HourFormat,
-    }
-
-    #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
-    #[serde(rename_all = "snake_case")]
-    enum HourFormat {
-        Hour12,
-        Hour24,
-    }
-
-    #[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
-    struct JournalSettingsJson {
-        pub path: Option<String>,
-        pub hour_format: Option<HourFormat>,
-    }
-
-    impl Settings for JournalSettings {
-        const KEY: Option<&'static str> = Some("journal");
-
-        type FileContent = JournalSettingsJson;
-
-        fn load(
-            default_value: &JournalSettingsJson,
-            user_values: &[&JournalSettingsJson],
-            _: &AppContext,
-        ) -> Result<Self> {
-            Self::load_via_json_merge(default_value, user_values)
-        }
-    }
-
-    #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
-    struct LanguageSettings {
-        #[serde(default)]
-        languages: HashMap<String, LanguageSettingEntry>,
-    }
-
-    #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
-    struct LanguageSettingEntry {
-        language_setting_1: Option<bool>,
-        language_setting_2: Option<bool>,
-    }
-
-    impl Settings for LanguageSettings {
-        const KEY: Option<&'static str> = None;
-
-        type FileContent = Self;
-
-        fn load(default_value: &Self, user_values: &[&Self], _: &AppContext) -> Result<Self> {
-            Self::load_via_json_merge(default_value, user_values)
-        }
-    }
-}
+// #[cfg(test)]
+// mod tests {
+//     use super::*;
+//     use serde_derive::Deserialize;
+//     use unindent::Unindent;
+
+//     #[gpui::test]
+//     fn test_settings_store_basic(cx: &mut AppContext) {
+//         let mut store = SettingsStore::default();
+//         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();
+
+//         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(),
+//             }
+//         );
+
+//         store
+//             .set_user_settings(
+//                 r#"{
+//                     "turbo": true,
+//                     "user": { "age": 31 },
+//                     "key1": "a"
+//                 }"#,
+//                 cx,
+//             )
+//             .unwrap();
+
+//         assert_eq!(store.get::<TurboSetting>(None), &TurboSetting(true));
+//         assert_eq!(
+//             store.get::<UserSettings>(None),
+//             &UserSettings {
+//                 name: "John Doe".to_string(),
+//                 age: 31,
+//                 staff: false
+//             }
+//         );
+
+//         store
+//             .set_local_settings(
+//                 1,
+//                 Path::new("/root1").into(),
+//                 Some(r#"{ "user": { "staff": true } }"#),
+//                 cx,
+//             )
+//             .unwrap();
+//         store
+//             .set_local_settings(
+//                 1,
+//                 Path::new("/root1/subdir").into(),
+//                 Some(r#"{ "user": { "name": "Jane Doe" } }"#),
+//                 cx,
+//             )
+//             .unwrap();
+
+//         store
+//             .set_local_settings(
+//                 1,
+//                 Path::new("/root2").into(),
+//                 Some(r#"{ "user": { "age": 42 }, "key2": "b" }"#),
+//                 cx,
+//             )
+//             .unwrap();
+
+//         assert_eq!(
+//             store.get::<UserSettings>(Some((1, Path::new("/root1/something")))),
+//             &UserSettings {
+//                 name: "John Doe".to_string(),
+//                 age: 31,
+//                 staff: true
+//             }
+//         );
+//         assert_eq!(
+//             store.get::<UserSettings>(Some((1, Path::new("/root1/subdir/something")))),
+//             &UserSettings {
+//                 name: "Jane Doe".to_string(),
+//                 age: 31,
+//                 staff: true
+//             }
+//         );
+//         assert_eq!(
+//             store.get::<UserSettings>(Some((1, Path::new("/root2/something")))),
+//             &UserSettings {
+//                 name: "John Doe".to_string(),
+//                 age: 42,
+//                 staff: false
+//             }
+//         );
+//         assert_eq!(
+//             store.get::<MultiKeySettings>(Some((1, 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 AppContext) {
+//         let mut store = SettingsStore::default();
+//         store
+//             .set_default_settings(
+//                 r#"{
+//                     "turbo": true,
+//                     "user": {
+//                         "name": "John Doe",
+//                         "age": 30,
+//                         "staff": false
+//                     },
+//                     "key1": "x"
+//                 }"#,
+//                 cx,
+//             )
+//             .unwrap();
+//         store
+//             .set_user_settings(r#"{ "turbo": false }"#, cx)
+//             .unwrap();
+//         store.register_setting::<UserSettings>(cx);
+//         store.register_setting::<TurboSetting>(cx);
+
+//         assert_eq!(store.get::<TurboSetting>(None), &TurboSetting(false));
+//         assert_eq!(
+//             store.get::<UserSettings>(None),
+//             &UserSettings {
+//                 name: "John Doe".to_string(),
+//                 age: 30,
+//                 staff: false,
+//             }
+//         );
+
+//         store.register_setting::<MultiKeySettings>(cx);
+//         assert_eq!(
+//             store.get::<MultiKeySettings>(None),
+//             &MultiKeySettings {
+//                 key1: "x".into(),
+//                 key2: String::new(),
+//             }
+//         );
+//     }
+
+//     #[gpui::test]
+//     fn test_setting_store_update(cx: &mut AppContext) {
+//         let mut store = SettingsStore::default();
+//         store.register_setting::<MultiKeySettings>(cx);
+//         store.register_setting::<UserSettings>(cx);
+//         store.register_setting::<LanguageSettings>(cx);
+
+//         // entries added and updated
+//         check_settings_update::<LanguageSettings>(
+//             &mut store,
+//             r#"{
+//                 "languages": {
+//                     "JSON": {
+//                         "language_setting_1": true
+//                     }
+//                 }
+//             }"#
+//             .unindent(),
+//             |settings| {
+//                 settings
+//                     .languages
+//                     .get_mut("JSON")
+//                     .unwrap()
+//                     .language_setting_1 = Some(false);
+//                 settings.languages.insert(
+//                     "Rust".into(),
+//                     LanguageSettingEntry {
+//                         language_setting_2: Some(true),
+//                         ..Default::default()
+//                     },
+//                 );
+//             },
+//             r#"{
+//                 "languages": {
+//                     "Rust": {
+//                         "language_setting_2": true
+//                     },
+//                     "JSON": {
+//                         "language_setting_1": false
+//                     }
+//                 }
+//             }"#
+//             .unindent(),
+//             cx,
+//         );
+
+//         // weird formatting
+//         check_settings_update::<UserSettings>(
+//             &mut store,
+//             r#"{
+//                 "user":   { "age": 36, "name": "Max", "staff": true }
+//             }"#
+//             .unindent(),
+//             |settings| settings.age = Some(37),
+//             r#"{
+//                 "user":   { "age": 37, "name": "Max", "staff": true }
+//             }"#
+//             .unindent(),
+//             cx,
+//         );
+
+//         // single-line formatting, other keys
+//         check_settings_update::<MultiKeySettings>(
+//             &mut store,
+//             r#"{ "one": 1, "two": 2 }"#.unindent(),
+//             |settings| settings.key1 = Some("x".into()),
+//             r#"{ "key1": "x", "one": 1, "two": 2 }"#.unindent(),
+//             cx,
+//         );
+
+//         // empty object
+//         check_settings_update::<UserSettings>(
+//             &mut store,
+//             r#"{
+//                 "user": {}
+//             }"#
+//             .unindent(),
+//             |settings| settings.age = Some(37),
+//             r#"{
+//                 "user": {
+//                     "age": 37
+//                 }
+//             }"#
+//             .unindent(),
+//             cx,
+//         );
+
+//         // no content
+//         check_settings_update::<UserSettings>(
+//             &mut store,
+//             r#""#.unindent(),
+//             |settings| settings.age = Some(37),
+//             r#"{
+//                 "user": {
+//                     "age": 37
+//                 }
+//             }
+//             "#
+//             .unindent(),
+//             cx,
+//         );
+
+//         check_settings_update::<UserSettings>(
+//             &mut store,
+//             r#"{
+//             }
+//             "#
+//             .unindent(),
+//             |settings| settings.age = Some(37),
+//             r#"{
+//                 "user": {
+//                     "age": 37
+//                 }
+//             }
+//             "#
+//             .unindent(),
+//             cx,
+//         );
+//     }
+
+//     fn check_settings_update<T: Setting>(
+//         store: &mut SettingsStore,
+//         old_json: String,
+//         update: fn(&mut T::FileContent),
+//         expected_new_json: String,
+//         cx: &mut AppContext,
+//     ) {
+//         store.set_user_settings(&old_json, cx).ok();
+//         let edits = store.edits_for_update::<T>(&old_json, update);
+//         let mut new_json = old_json;
+//         for (range, replacement) in edits.into_iter() {
+//             new_json.replace_range(range, &replacement);
+//         }
+//         pretty_assertions::assert_eq!(new_json, expected_new_json);
+//     }
+
+//     #[derive(Debug, PartialEq, Deserialize)]
+//     struct UserSettings {
+//         name: String,
+//         age: u32,
+//         staff: bool,
+//     }
+
+//     #[derive(Default, Clone, Serialize, Deserialize, JsonSchema)]
+//     struct UserSettingsJson {
+//         name: Option<String>,
+//         age: Option<u32>,
+//         staff: Option<bool>,
+//     }
+
+//     impl Setting for UserSettings {
+//         const KEY: Option<&'static str> = Some("user");
+//         type FileContent = UserSettingsJson;
+
+//         fn load(
+//             default_value: &UserSettingsJson,
+//             user_values: &[&UserSettingsJson],
+//             _: &AppContext,
+//         ) -> Result<Self> {
+//             Self::load_via_json_merge(default_value, user_values)
+//         }
+//     }
+
+//     #[derive(Debug, Deserialize, PartialEq)]
+//     struct TurboSetting(bool);
+
+//     impl Setting for TurboSetting {
+//         const KEY: Option<&'static str> = Some("turbo");
+//         type FileContent = Option<bool>;
+
+//         fn load(
+//             default_value: &Option<bool>,
+//             user_values: &[&Option<bool>],
+//             _: &AppContext,
+//         ) -> Result<Self> {
+//             Self::load_via_json_merge(default_value, user_values)
+//         }
+//     }
+
+//     #[derive(Clone, Debug, PartialEq, Deserialize)]
+//     struct MultiKeySettings {
+//         #[serde(default)]
+//         key1: String,
+//         #[serde(default)]
+//         key2: String,
+//     }
+
+//     #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
+//     struct MultiKeySettingsJson {
+//         key1: Option<String>,
+//         key2: Option<String>,
+//     }
+
+//     impl Setting for MultiKeySettings {
+//         const KEY: Option<&'static str> = None;
+
+//         type FileContent = MultiKeySettingsJson;
+
+//         fn load(
+//             default_value: &MultiKeySettingsJson,
+//             user_values: &[&MultiKeySettingsJson],
+//             _: &AppContext,
+//         ) -> Result<Self> {
+//             Self::load_via_json_merge(default_value, user_values)
+//         }
+//     }
+
+//     #[derive(Debug, Deserialize)]
+//     struct JournalSettings {
+//         pub path: String,
+//         pub hour_format: HourFormat,
+//     }
+
+//     #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
+//     #[serde(rename_all = "snake_case")]
+//     enum HourFormat {
+//         Hour12,
+//         Hour24,
+//     }
+
+//     #[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
+//     struct JournalSettingsJson {
+//         pub path: Option<String>,
+//         pub hour_format: Option<HourFormat>,
+//     }
+
+//     impl Setting for JournalSettings {
+//         const KEY: Option<&'static str> = Some("journal");
+
+//         type FileContent = JournalSettingsJson;
+
+//         fn load(
+//             default_value: &JournalSettingsJson,
+//             user_values: &[&JournalSettingsJson],
+//             _: &AppContext,
+//         ) -> Result<Self> {
+//             Self::load_via_json_merge(default_value, user_values)
+//         }
+//     }
+
+//     #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
+//     struct LanguageSettings {
+//         #[serde(default)]
+//         languages: HashMap<String, LanguageSettingEntry>,
+//     }
+
+//     #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
+//     struct LanguageSettingEntry {
+//         language_setting_1: Option<bool>,
+//         language_setting_2: Option<bool>,
+//     }
+
+//     impl Setting for LanguageSettings {
+//         const KEY: Option<&'static str> = None;
+
+//         type FileContent = Self;
+
+//         fn load(default_value: &Self, user_values: &[&Self], _: &AppContext) -> Result<Self> {
+//             Self::load_via_json_merge(default_value, user_values)
+//         }
+//     }
+// }

crates/theme2/src/settings.rs 🔗

@@ -8,7 +8,7 @@ use schemars::{
 };
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
-use settings2::SettingsJsonSchemaParams;
+use settings2::{Settings, SettingsJsonSchemaParams};
 use std::sync::Arc;
 use util::ResultExt as _;
 

crates/theme2/src/theme2.rs 🔗

@@ -6,6 +6,7 @@ pub use registry::*;
 pub use settings::*;
 
 use gpui2::{AppContext, HighlightStyle, Hsla, SharedString};
+use settings2::Settings;
 use std::sync::Arc;
 
 pub fn init(cx: &mut AppContext) {