diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 322fd19b9e..37377b6eef 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -234,6 +234,14 @@ impl LanguageRegistry { .cloned() } + pub fn language_names(&self) -> Vec { + self.languages + .read() + .iter() + .map(|language| language.name().to_string()) + .collect() + } + pub fn select_language(&self, path: impl AsRef) -> Option> { let path = path.as_ref(); let filename = path.file_name().and_then(|name| name.to_str()); diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 3dcb55b2fe..98733e6920 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -2,10 +2,14 @@ use anyhow::Result; use gpui::font_cache::{FamilyId, FontCache}; use schemars::{ gen::{SchemaGenerator, SchemaSettings}, + schema::{ + InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec, SubschemaValidation, + }, JsonSchema, }; use serde::Deserialize; -use std::{collections::HashMap, fmt::Display, sync::Arc}; +use serde_json::Value; +use std::{collections::HashMap, fmt::Display, os::unix::fs::chroot, sync::Arc}; use theme::{Theme, ThemeRegistry}; use util::ResultExt as _; @@ -36,53 +40,6 @@ pub enum SoftWrap { PreferredLineLength, } -#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] -pub enum ThemeNames { - Dark, - Light, -} - -impl Display for ThemeNames { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ThemeNames::Dark => write!(f, "Dark"), - ThemeNames::Light => write!(f, "Light"), - } - } -} - -#[derive(Clone, Debug, Deserialize, Default, JsonSchema)] -pub struct LanguageOverrides { - C: Option, - JSON: Option, - Markdown: Option, - PlainText: Option, - Rust: Option, - TSX: Option, - TypeScript: Option, -} - -impl IntoIterator for LanguageOverrides { - type Item = (String, LanguageOverride); - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - vec![ - ("C", self.C), - ("JSON", self.JSON), - ("Markdown", self.Markdown), - ("PlainText", self.PlainText), - ("Rust", self.Rust), - ("TSX", self.TSX), - ("TypeScript", self.TypeScript), - ] - .into_iter() - .filter_map(|(name, language_override)| language_override.map(|lo| (name.to_owned(), lo))) - .collect::>() - .into_iter() - } -} - #[derive(Clone, Debug, Default, Deserialize, JsonSchema)] pub struct SettingsFileContent { #[serde(default)] @@ -94,9 +51,9 @@ pub struct SettingsFileContent { #[serde(flatten)] pub editor: LanguageOverride, #[serde(default)] - pub language_overrides: LanguageOverrides, + pub language_overrides: HashMap, LanguageOverride>, #[serde(default)] - pub theme: Option, + pub theme: Option, } impl Settings { @@ -117,13 +74,88 @@ impl Settings { }) } - pub fn file_json_schema() -> serde_json::Value { + pub fn file_json_schema( + theme_names: Vec, + language_names: Vec, + ) -> serde_json::Value { let settings = SchemaSettings::draft07().with(|settings| { - settings.option_nullable = true; settings.option_add_null_type = false; }); let generator = SchemaGenerator::new(settings); - serde_json::to_value(generator.into_root_schema_for::()).unwrap() + let mut root_schema = generator.into_root_schema_for::(); + + // Construct theme names reference type + let theme_names = theme_names + .into_iter() + .map(|name| Value::String(name)) + .collect(); + let theme_names_schema = Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), + enum_values: Some(theme_names), + ..Default::default() + }); + root_schema + .definitions + .insert("ThemeName".to_owned(), theme_names_schema); + + // Construct language overrides reference type + let language_override_schema_reference = Schema::Object(SchemaObject { + reference: Some("#/definitions/LanguageOverride".to_owned()), + ..Default::default() + }); + let language_overrides_properties = language_names + .into_iter() + .map(|name| { + ( + name, + Schema::Object(SchemaObject { + subschemas: Some(Box::new(SubschemaValidation { + all_of: Some(vec![language_override_schema_reference.clone()]), + ..Default::default() + })), + ..Default::default() + }), + ) + }) + .collect(); + let language_overrides_schema = Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))), + object: Some(Box::new(ObjectValidation { + properties: language_overrides_properties, + ..Default::default() + })), + ..Default::default() + }); + root_schema + .definitions + .insert("LanguageOverrides".to_owned(), language_overrides_schema); + + // Modify theme property to use new theme reference type + let settings_file_schema = root_schema.schema.object.as_mut().unwrap(); + let language_overrides_schema_reference = Schema::Object(SchemaObject { + reference: Some("#/definitions/ThemeName".to_owned()), + ..Default::default() + }); + settings_file_schema.properties.insert( + "theme".to_owned(), + Schema::Object(SchemaObject { + subschemas: Some(Box::new(SubschemaValidation { + all_of: Some(vec![language_overrides_schema_reference]), + ..Default::default() + })), + ..Default::default() + }), + ); + + // Modify language_overrides property to use LanguageOverrides reference + settings_file_schema.properties.insert( + "language_overrides".to_owned(), + Schema::Object(SchemaObject { + reference: Some("#/definitions/LanguageOverrides".to_owned()), + ..Default::default() + }), + ); + serde_json::to_value(root_schema).unwrap() } pub fn with_overrides( diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 86a059e6fd..976f78437c 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -137,13 +137,16 @@ pub fn build_workspace( let mut workspace = Workspace::new(&workspace_params, cx); let project = workspace.project().clone(); + let theme_names = app_state.themes.list().collect(); + let language_names = app_state.languages.language_names(); + project.update(cx, |project, _| { project.set_language_server_settings(serde_json::json!({ "json": { "schemas": [ { "fileMatch": "**/.zed/settings.json", - "schema": Settings::file_json_schema(), + "schema": Settings::file_json_schema(theme_names, language_names), } ] }