From 035cd37aa86917c43bc73a0d584751dbf9be865e Mon Sep 17 00:00:00 2001 From: Keith Simmons Date: Thu, 7 Apr 2022 16:00:51 -0700 Subject: [PATCH 1/3] Add explicit types for LanguageOverrides and Themes to SettingsFileContent in order to improve completions in zed settings file --- crates/settings/src/settings.rs | 71 +++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index c897586017..3dcb55b2fe 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -1,8 +1,11 @@ use anyhow::Result; use gpui::font_cache::{FamilyId, FontCache}; -use schemars::{schema_for, JsonSchema}; +use schemars::{ + gen::{SchemaGenerator, SchemaSettings}, + JsonSchema, +}; use serde::Deserialize; -use std::{collections::HashMap, sync::Arc}; +use std::{collections::HashMap, fmt::Display, sync::Arc}; use theme::{Theme, ThemeRegistry}; use util::ResultExt as _; @@ -33,6 +36,53 @@ 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)] @@ -44,9 +94,9 @@ pub struct SettingsFileContent { #[serde(flatten)] pub editor: LanguageOverride, #[serde(default)] - pub language_overrides: HashMap, LanguageOverride>, + pub language_overrides: LanguageOverrides, #[serde(default)] - pub theme: Option, + pub theme: Option, } impl Settings { @@ -68,7 +118,12 @@ impl Settings { } pub fn file_json_schema() -> serde_json::Value { - serde_json::to_value(schema_for!(SettingsFileContent)).unwrap() + 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() } pub fn with_overrides( @@ -128,7 +183,7 @@ impl Settings { } } if let Some(value) = &data.theme { - if let Some(theme) = theme_registry.get(value).log_err() { + if let Some(theme) = theme_registry.get(&value.to_string()).log_err() { self.theme = theme; } } @@ -142,10 +197,10 @@ impl Settings { data.editor.preferred_line_length, ); - for (language_name, settings) in &data.language_overrides { + for (language_name, settings) in data.language_overrides.clone().into_iter() { let target = self .language_overrides - .entry(language_name.clone()) + .entry(language_name.into()) .or_default(); merge_option(&mut target.tab_size, settings.tab_size); From 3e40b5beada237320a379c8e07c1ad9ece165c49 Mon Sep 17 00:00:00 2001 From: Keith Simmons Date: Thu, 7 Apr 2022 18:15:02 -0700 Subject: [PATCH 2/3] dynamically inject theme names and language properties into schema --- crates/language/src/language.rs | 8 ++ crates/settings/src/settings.rs | 138 ++++++++++++++++++++------------ crates/zed/src/zed.rs | 5 +- 3 files changed, 97 insertions(+), 54 deletions(-) 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), } ] } From 56fcffb6345157b6ebd2be73cf3fa2cbc2888f3b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 9 Apr 2022 07:55:23 -0600 Subject: [PATCH 3/3] Remove unused imports --- crates/settings/src/settings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 98733e6920..622d73e663 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -9,7 +9,7 @@ use schemars::{ }; use serde::Deserialize; use serde_json::Value; -use std::{collections::HashMap, fmt::Display, os::unix::fs::chroot, sync::Arc}; +use std::{collections::HashMap, sync::Arc}; use theme::{Theme, ThemeRegistry}; use util::ResultExt as _;