Customize language settings JSON schema in language crate

Max Brunsfeld created

Change summary

crates/language/src/language_settings.rs | 60 +++++++++++++++++++++++++
crates/settings/src/settings.rs          | 57 +++++-------------------
crates/settings/src/settings_store.rs    |  7 +-
crates/zed/src/main.rs                   |  1 
4 files changed, 77 insertions(+), 48 deletions(-)

Detailed changes

crates/language/src/language_settings.rs 🔗

@@ -1,7 +1,10 @@
 use anyhow::Result;
 use collections::HashMap;
 use gpui::AppContext;
-use schemars::JsonSchema;
+use schemars::{
+    schema::{InstanceType, ObjectValidation, Schema, SchemaObject},
+    JsonSchema,
+};
 use serde::{Deserialize, Serialize};
 use std::{num::NonZeroU32, path::Path, sync::Arc};
 
@@ -247,6 +250,61 @@ impl settings::Setting for AllLanguageSettings {
             languages,
         })
     }
+
+    fn json_schema(
+        generator: &mut schemars::gen::SchemaGenerator,
+        params: &settings::SettingsJsonSchemaParams,
+    ) -> schemars::schema::RootSchema {
+        let mut root_schema = generator.root_schema_for::<Self::FileContent>();
+
+        // Create a schema for a 'languages overrides' object, associating editor
+        // settings with specific langauges.
+        assert!(root_schema
+            .definitions
+            .contains_key("LanguageSettingsContent"));
+
+        let languages_object_schema = SchemaObject {
+            instance_type: Some(InstanceType::Object.into()),
+            object: Some(Box::new(ObjectValidation {
+                properties: params
+                    .language_names
+                    .iter()
+                    .map(|name| {
+                        (
+                            name.clone(),
+                            Schema::new_ref("#/definitions/LanguageSettingsContent".into()),
+                        )
+                    })
+                    .collect(),
+                ..Default::default()
+            })),
+            ..Default::default()
+        };
+
+        root_schema
+            .definitions
+            .extend([("Languages".into(), languages_object_schema.into())]);
+
+        root_schema
+            .schema
+            .object
+            .as_mut()
+            .unwrap()
+            .properties
+            .extend([
+                (
+                    "languages".to_owned(),
+                    Schema::new_ref("#/definitions/Languages".into()),
+                ),
+                // For backward compatibility
+                (
+                    "language_overrides".to_owned(),
+                    Schema::new_ref("#/definitions/Languages".into()),
+                ),
+            ]);
+
+        root_schema
+    }
 }
 
 fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent) {

crates/settings/src/settings.rs 🔗

@@ -10,7 +10,7 @@ use gpui::{
 };
 use schemars::{
     gen::SchemaGenerator,
-    schema::{InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec},
+    schema::{InstanceType, Schema, SchemaObject},
     JsonSchema,
 };
 use serde::{Deserialize, Serialize};
@@ -80,7 +80,7 @@ impl Setting for Settings {
 
         // Create a schema for a theme name.
         let theme_name_schema = SchemaObject {
-            instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
+            instance_type: Some(InstanceType::String.into()),
             enum_values: Some(
                 params
                     .theme_names
@@ -92,51 +92,20 @@ impl Setting for Settings {
             ..Default::default()
         };
 
-        // Create a schema for a 'languages overrides' object, associating editor
-        // settings with specific langauges.
-        assert!(root_schema.definitions.contains_key("EditorSettings"));
-
-        let languages_object_schema = SchemaObject {
-            instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))),
-            object: Some(Box::new(ObjectValidation {
-                properties: params
-                    .language_names
-                    .iter()
-                    .map(|name| {
-                        (
-                            name.clone(),
-                            Schema::new_ref("#/definitions/EditorSettings".into()),
-                        )
-                    })
-                    .collect(),
-                ..Default::default()
-            })),
-            ..Default::default()
-        };
-
-        // Add these new schemas as definitions, and modify properties of the root
-        // schema to reference them.
-        root_schema.definitions.extend([
-            ("ThemeName".into(), theme_name_schema.into()),
-            ("Languages".into(), languages_object_schema.into()),
-        ]);
-        let root_schema_object = &mut root_schema.schema.object.as_mut().unwrap();
+        root_schema
+            .definitions
+            .extend([("ThemeName".into(), theme_name_schema.into())]);
 
-        root_schema_object.properties.extend([
-            (
+        root_schema
+            .schema
+            .object
+            .as_mut()
+            .unwrap()
+            .properties
+            .extend([(
                 "theme".to_owned(),
                 Schema::new_ref("#/definitions/ThemeName".into()),
-            ),
-            (
-                "languages".to_owned(),
-                Schema::new_ref("#/definitions/Languages".into()),
-            ),
-            // For backward compatibility
-            (
-                "language_overrides".to_owned(),
-                Schema::new_ref("#/definitions/Languages".into()),
-            ),
-        ]);
+            )]);
 
         root_schema
     }

crates/settings/src/settings_store.rs 🔗

@@ -308,7 +308,8 @@ impl SettingsStore {
         default_settings_content: &str,
         cx: &AppContext,
     ) -> Result<()> {
-        self.default_deserialized_settings = Some(serde_json::from_str(default_settings_content)?);
+        self.default_deserialized_settings =
+            Some(parse_json_with_comments(default_settings_content)?);
         self.recompute_values(None, cx)?;
         Ok(())
     }
@@ -319,7 +320,7 @@ impl SettingsStore {
         user_settings_content: &str,
         cx: &AppContext,
     ) -> Result<()> {
-        self.user_deserialized_settings = Some(serde_json::from_str(user_settings_content)?);
+        self.user_deserialized_settings = Some(parse_json_with_comments(user_settings_content)?);
         self.recompute_values(None, cx)?;
         Ok(())
     }
@@ -333,7 +334,7 @@ impl SettingsStore {
     ) -> Result<()> {
         if let Some(content) = settings_content {
             self.local_deserialized_settings
-                .insert(path.clone(), serde_json::from_str(content)?);
+                .insert(path.clone(), parse_json_with_comments(content)?);
         } else {
             self.local_deserialized_settings.remove(&path);
         }

crates/zed/src/main.rs 🔗

@@ -160,6 +160,7 @@ fn main() {
         project::Project::init(&client, cx);
         client::init(&client, cx);
         command_palette::init(cx);
+        language::init(cx);
         editor::init(cx);
         go_to_line::init(cx);
         file_finder::init(cx);