Detailed changes
@@ -4133,7 +4133,7 @@ dependencies = [
[[package]]
name = "dap-types"
version = "0.0.1"
-source = "git+https://github.com/zed-industries/dap-types?rev=b40956a7f4d1939da67429d941389ee306a3a308#b40956a7f4d1939da67429d941389ee306a3a308"
+source = "git+https://github.com/zed-industries/dap-types?rev=7f39295b441614ca9dbf44293e53c32f666897f9#7f39295b441614ca9dbf44293e53c32f666897f9"
dependencies = [
"schemars",
"serde",
@@ -8847,6 +8847,7 @@ dependencies = [
"http_client",
"imara-diff",
"indoc",
+ "inventory",
"itertools 0.14.0",
"log",
"lsp",
@@ -14053,12 +14054,13 @@ dependencies = [
[[package]]
name = "schemars"
-version = "0.8.22"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615"
+checksum = "fe8c9d1c68d67dd9f97ecbc6f932b60eb289c5dbddd8aa1405484a8fd2fcd984"
dependencies = [
"dyn-clone",
"indexmap",
+ "ref-cast",
"schemars_derive",
"serde",
"serde_json",
@@ -14066,9 +14068,9 @@ dependencies = [
[[package]]
name = "schemars_derive"
-version = "0.8.22"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d"
+checksum = "6ca9fcb757952f8e8629b9ab066fc62da523c46c2b247b1708a3be06dd82530b"
dependencies = [
"proc-macro2",
"quote",
@@ -16010,6 +16012,7 @@ dependencies = [
"futures 0.3.31",
"gpui",
"indexmap",
+ "inventory",
"log",
"palette",
"parking_lot",
@@ -444,7 +444,7 @@ core-video = { version = "0.4.3", features = ["metal"] }
cpal = "0.16"
criterion = { version = "0.5", features = ["html_reports"] }
ctor = "0.4.0"
-dap-types = { git = "https://github.com/zed-industries/dap-types", rev = "b40956a7f4d1939da67429d941389ee306a3a308" }
+dap-types = { git = "https://github.com/zed-industries/dap-types", rev = "7f39295b441614ca9dbf44293e53c32f666897f9" }
dashmap = "6.0"
derive_more = "0.99.17"
dirs = "4.0"
@@ -540,7 +540,7 @@ rustc-hash = "2.1.0"
rustls = { version = "0.23.26" }
rustls-platform-verifier = "0.5.0"
scap = { git = "https://github.com/zed-industries/scap", rev = "08f0a01417505cc0990b9931a37e5120db92e0d0", default-features = false }
-schemars = { version = "0.8", features = ["impl_json_schema", "indexmap2"] }
+schemars = { version = "1.0", features = ["indexmap2"] }
semver = "1.0"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
@@ -6,9 +6,10 @@ use anyhow::{Result, bail};
use collections::IndexMap;
use gpui::{App, Pixels, SharedString};
use language_model::LanguageModel;
-use schemars::{JsonSchema, schema::Schema};
+use schemars::{JsonSchema, json_schema};
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsSources};
+use std::borrow::Cow;
pub use crate::agent_profile::*;
@@ -321,29 +322,27 @@ pub struct LanguageModelSelection {
pub struct LanguageModelProviderSetting(pub String);
impl JsonSchema for LanguageModelProviderSetting {
- fn schema_name() -> String {
+ fn schema_name() -> Cow<'static, str> {
"LanguageModelProviderSetting".into()
}
- fn json_schema(_: &mut schemars::r#gen::SchemaGenerator) -> Schema {
- schemars::schema::SchemaObject {
- enum_values: Some(vec![
- "anthropic".into(),
- "amazon-bedrock".into(),
- "google".into(),
- "lmstudio".into(),
- "ollama".into(),
- "openai".into(),
- "zed.dev".into(),
- "copilot_chat".into(),
- "deepseek".into(),
- "openrouter".into(),
- "mistral".into(),
- "vercel".into(),
- ]),
- ..Default::default()
- }
- .into()
+ fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
+ json_schema!({
+ "enum": [
+ "anthropic",
+ "amazon-bedrock",
+ "google",
+ "lmstudio",
+ "ollama",
+ "openai",
+ "zed.dev",
+ "copilot_chat",
+ "deepseek",
+ "openrouter",
+ "mistral",
+ "vercel"
+ ]
+ })
}
}
@@ -1,8 +1,9 @@
use anyhow::Result;
use language_model::LanguageModelToolSchemaFormat;
use schemars::{
- JsonSchema,
- schema::{RootSchema, Schema, SchemaObject},
+ JsonSchema, Schema,
+ generate::SchemaSettings,
+ transform::{Transform, transform_subschemas},
};
pub fn json_schema_for<T: JsonSchema>(
@@ -13,7 +14,7 @@ pub fn json_schema_for<T: JsonSchema>(
}
fn schema_to_json(
- schema: &RootSchema,
+ schema: &Schema,
format: LanguageModelToolSchemaFormat,
) -> Result<serde_json::Value> {
let mut value = serde_json::to_value(schema)?;
@@ -21,58 +22,42 @@ fn schema_to_json(
Ok(value)
}
-fn root_schema_for<T: JsonSchema>(format: LanguageModelToolSchemaFormat) -> RootSchema {
+fn root_schema_for<T: JsonSchema>(format: LanguageModelToolSchemaFormat) -> Schema {
let mut generator = match format {
- LanguageModelToolSchemaFormat::JsonSchema => schemars::SchemaGenerator::default(),
- LanguageModelToolSchemaFormat::JsonSchemaSubset => {
- schemars::r#gen::SchemaSettings::default()
- .with(|settings| {
- settings.meta_schema = None;
- settings.inline_subschemas = true;
- settings
- .visitors
- .push(Box::new(TransformToJsonSchemaSubsetVisitor));
- })
- .into_generator()
- }
+ LanguageModelToolSchemaFormat::JsonSchema => SchemaSettings::draft07().into_generator(),
+ // TODO: Gemini docs mention using a subset of OpenAPI 3, so this may benefit from using
+ // `SchemaSettings::openapi3()`.
+ LanguageModelToolSchemaFormat::JsonSchemaSubset => SchemaSettings::draft07()
+ .with(|settings| {
+ settings.meta_schema = None;
+ settings.inline_subschemas = true;
+ })
+ .with_transform(ToJsonSchemaSubsetTransform)
+ .into_generator(),
};
generator.root_schema_for::<T>()
}
#[derive(Debug, Clone)]
-struct TransformToJsonSchemaSubsetVisitor;
-
-impl schemars::visit::Visitor for TransformToJsonSchemaSubsetVisitor {
- fn visit_root_schema(&mut self, root: &mut RootSchema) {
- schemars::visit::visit_root_schema(self, root)
- }
+struct ToJsonSchemaSubsetTransform;
- fn visit_schema(&mut self, schema: &mut Schema) {
- schemars::visit::visit_schema(self, schema)
- }
-
- fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
+impl Transform for ToJsonSchemaSubsetTransform {
+ fn transform(&mut self, schema: &mut Schema) {
// Ensure that the type field is not an array, this happens when we use
// Option<T>, the type will be [T, "null"].
- if let Some(instance_type) = schema.instance_type.take() {
- schema.instance_type = match instance_type {
- schemars::schema::SingleOrVec::Single(t) => {
- Some(schemars::schema::SingleOrVec::Single(t))
+ if let Some(type_field) = schema.get_mut("type") {
+ if let Some(types) = type_field.as_array() {
+ if let Some(first_type) = types.first() {
+ *type_field = first_type.clone();
}
- schemars::schema::SingleOrVec::Vec(items) => items
- .into_iter()
- .next()
- .map(schemars::schema::SingleOrVec::from),
- };
+ }
}
- // One of is not supported, use anyOf instead.
- if let Some(subschema) = schema.subschemas.as_mut() {
- if let Some(one_of) = subschema.one_of.take() {
- subschema.any_of = Some(one_of);
- }
+ // oneOf is not supported, use anyOf instead
+ if let Some(one_of) = schema.remove("oneOf") {
+ schema.insert("anyOf".to_string(), one_of);
}
- schemars::visit::visit_schema_object(self, schema)
+ transform_subschemas(self, schema);
}
}
@@ -22,9 +22,7 @@ use gpui::{
use language::{
Diagnostic, DiagnosticEntry, DiagnosticSourceKind, FakeLspAdapter, Language, LanguageConfig,
LanguageMatcher, LineEnding, OffsetRangeExt, Point, Rope,
- language_settings::{
- AllLanguageSettings, Formatter, FormatterList, PrettierSettings, SelectedFormatter,
- },
+ language_settings::{AllLanguageSettings, Formatter, PrettierSettings, SelectedFormatter},
tree_sitter_rust, tree_sitter_typescript,
};
use lsp::{LanguageServerId, OneOf};
@@ -4591,15 +4589,13 @@ async fn test_formatting_buffer(
cx_a.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
- file.defaults.formatter = Some(SelectedFormatter::List(FormatterList(
- vec![Formatter::External {
+ file.defaults.formatter =
+ Some(SelectedFormatter::List(vec![Formatter::External {
command: "awk".into(),
arguments: Some(
vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
),
- }]
- .into(),
- )));
+ }]));
});
});
});
@@ -4699,9 +4695,10 @@ async fn test_prettier_formatting_buffer(
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
- file.defaults.formatter = Some(SelectedFormatter::List(FormatterList(
- vec![Formatter::LanguageServer { name: None }].into(),
- )));
+ file.defaults.formatter =
+ Some(SelectedFormatter::List(vec![Formatter::LanguageServer {
+ name: None,
+ }]));
file.defaults.prettier = Some(PrettierSettings {
allowed: true,
..PrettierSettings::default()
@@ -14,8 +14,7 @@ use http_client::BlockedHttpClient;
use language::{
FakeLspAdapter, Language, LanguageConfig, LanguageMatcher, LanguageRegistry,
language_settings::{
- AllLanguageSettings, Formatter, FormatterList, PrettierSettings, SelectedFormatter,
- language_settings,
+ AllLanguageSettings, Formatter, PrettierSettings, SelectedFormatter, language_settings,
},
tree_sitter_typescript,
};
@@ -505,9 +504,10 @@ async fn test_ssh_collaboration_formatting_with_prettier(
cx_b.update(|cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
- file.defaults.formatter = Some(SelectedFormatter::List(FormatterList(
- vec![Formatter::LanguageServer { name: None }].into(),
- )));
+ file.defaults.formatter =
+ Some(SelectedFormatter::List(vec![Formatter::LanguageServer {
+ name: None,
+ }]));
file.defaults.prettier = Some(PrettierSettings {
allowed: true,
..PrettierSettings::default()
@@ -10,6 +10,7 @@ use gpui::{AsyncApp, SharedString};
pub use http_client::{HttpClient, github::latest_github_release};
use language::{LanguageName, LanguageToolchainStore};
use node_runtime::NodeRuntime;
+use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::WorktreeId;
use smol::fs::File;
@@ -47,7 +48,10 @@ pub trait DapDelegate: Send + Sync + 'static {
async fn shell_env(&self) -> collections::HashMap<String, String>;
}
-#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
+#[derive(
+ Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize, JsonSchema,
+)]
+#[serde(transparent)]
pub struct DebugAdapterName(pub SharedString);
impl Deref for DebugAdapterName {
@@ -3,7 +3,7 @@ use std::sync::Arc;
use gpui::{App, FontFeatures, FontWeight};
use project::project_settings::{InlineBlameSettings, ProjectSettings};
use settings::{EditableSettingControl, Settings};
-use theme::{FontFamilyCache, ThemeSettings};
+use theme::{FontFamilyCache, FontFamilyName, ThemeSettings};
use ui::{
CheckboxWithLabel, ContextMenu, DropdownMenu, NumericStepper, SettingsContainer, SettingsGroup,
prelude::*,
@@ -75,7 +75,7 @@ impl EditableSettingControl for BufferFontFamilyControl {
value: Self::Value,
_cx: &App,
) {
- settings.buffer_font_family = Some(value.to_string());
+ settings.buffer_font_family = Some(FontFamilyName(value.into()));
}
}
@@ -30,7 +30,7 @@ use language::{
},
tree_sitter_python,
};
-use language_settings::{Formatter, FormatterList, IndentGuideSettings};
+use language_settings::{Formatter, IndentGuideSettings};
use lsp::CompletionParams;
use multi_buffer::{IndentGuide, PathKey};
use parking_lot::Mutex;
@@ -3567,7 +3567,7 @@ async fn test_indent_outdent_with_hard_tabs(cx: &mut TestAppContext) {
#[gpui::test]
fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
init_test(cx, |settings| {
- settings.languages.extend([
+ settings.languages.0.extend([
(
"TOML".into(),
LanguageSettingsContent {
@@ -5145,7 +5145,7 @@ fn test_transpose(cx: &mut TestAppContext) {
#[gpui::test]
async fn test_rewrap(cx: &mut TestAppContext) {
init_test(cx, |settings| {
- settings.languages.extend([
+ settings.languages.0.extend([
(
"Markdown".into(),
LanguageSettingsContent {
@@ -9326,7 +9326,7 @@ async fn test_document_format_during_save(cx: &mut TestAppContext) {
// Set rust language override and assert overridden tabsize is sent to language server
update_test_language_settings(cx, |settings| {
- settings.languages.insert(
+ settings.languages.0.insert(
"Rust".into(),
LanguageSettingsContent {
tab_size: NonZeroU32::new(8),
@@ -9890,7 +9890,7 @@ async fn test_range_format_during_save(cx: &mut TestAppContext) {
// Set Rust language override and assert overridden tabsize is sent to language server
update_test_language_settings(cx, |settings| {
- settings.languages.insert(
+ settings.languages.0.insert(
"Rust".into(),
LanguageSettingsContent {
tab_size: NonZeroU32::new(8),
@@ -9933,9 +9933,9 @@ async fn test_range_format_during_save(cx: &mut TestAppContext) {
#[gpui::test]
async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
init_test(cx, |settings| {
- settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
- FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
- ))
+ settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(vec![
+ Formatter::LanguageServer { name: None },
+ ]))
});
let fs = FakeFs::new(cx.executor());
@@ -10062,21 +10062,17 @@ async fn test_document_format_manual_trigger(cx: &mut TestAppContext) {
async fn test_multiple_formatters(cx: &mut TestAppContext) {
init_test(cx, |settings| {
settings.defaults.remove_trailing_whitespace_on_save = Some(true);
- settings.defaults.formatter =
- Some(language_settings::SelectedFormatter::List(FormatterList(
- vec![
- Formatter::LanguageServer { name: None },
- Formatter::CodeActions(
- [
- ("code-action-1".into(), true),
- ("code-action-2".into(), true),
- ]
- .into_iter()
- .collect(),
- ),
+ settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(vec![
+ Formatter::LanguageServer { name: None },
+ Formatter::CodeActions(
+ [
+ ("code-action-1".into(), true),
+ ("code-action-2".into(), true),
]
- .into(),
- )))
+ .into_iter()
+ .collect(),
+ ),
+ ]))
});
let fs = FakeFs::new(cx.executor());
@@ -10328,9 +10324,9 @@ async fn test_multiple_formatters(cx: &mut TestAppContext) {
#[gpui::test]
async fn test_organize_imports_manual_trigger(cx: &mut TestAppContext) {
init_test(cx, |settings| {
- settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
- FormatterList(vec![Formatter::LanguageServer { name: None }].into()),
- ))
+ settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(vec![
+ Formatter::LanguageServer { name: None },
+ ]))
});
let fs = FakeFs::new(cx.executor());
@@ -14905,7 +14901,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut TestAppCon
.unwrap();
let _fake_server = fake_servers.next().await.unwrap();
update_test_language_settings(cx, |language_settings| {
- language_settings.languages.insert(
+ language_settings.languages.0.insert(
language_name.clone(),
LanguageSettingsContent {
tab_size: NonZeroU32::new(8),
@@ -15803,9 +15799,9 @@ fn completion_menu_entries(menu: &CompletionsMenu) -> Vec<String> {
#[gpui::test]
async fn test_document_format_with_prettier(cx: &mut TestAppContext) {
init_test(cx, |settings| {
- settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(
- FormatterList(vec![Formatter::Prettier].into()),
- ))
+ settings.defaults.formatter = Some(language_settings::SelectedFormatter::List(vec![
+ Formatter::Prettier,
+ ]))
});
let fs = FakeFs::new(cx.executor());
@@ -125,9 +125,7 @@ pub trait Action: Any + Send {
Self: Sized;
/// Optional JSON schema for the action's input data.
- fn action_json_schema(
- _: &mut schemars::r#gen::SchemaGenerator,
- ) -> Option<schemars::schema::Schema>
+ fn action_json_schema(_: &mut schemars::SchemaGenerator) -> Option<schemars::Schema>
where
Self: Sized,
{
@@ -238,7 +236,7 @@ impl Default for ActionRegistry {
struct ActionData {
pub build: ActionBuilder,
- pub json_schema: fn(&mut schemars::r#gen::SchemaGenerator) -> Option<schemars::schema::Schema>,
+ pub json_schema: fn(&mut schemars::SchemaGenerator) -> Option<schemars::Schema>,
}
/// This type must be public so that our macros can build it in other crates.
@@ -253,7 +251,7 @@ pub struct MacroActionData {
pub name: &'static str,
pub type_id: TypeId,
pub build: ActionBuilder,
- pub json_schema: fn(&mut schemars::r#gen::SchemaGenerator) -> Option<schemars::schema::Schema>,
+ pub json_schema: fn(&mut schemars::SchemaGenerator) -> Option<schemars::Schema>,
pub deprecated_aliases: &'static [&'static str],
pub deprecation_message: Option<&'static str>,
}
@@ -357,8 +355,8 @@ impl ActionRegistry {
pub fn action_schemas(
&self,
- generator: &mut schemars::r#gen::SchemaGenerator,
- ) -> Vec<(&'static str, Option<schemars::schema::Schema>)> {
+ generator: &mut schemars::SchemaGenerator,
+ ) -> Vec<(&'static str, Option<schemars::Schema>)> {
// Use the order from all_names so that the resulting schema has sensible order.
self.all_names
.iter()
@@ -1388,8 +1388,8 @@ impl App {
/// Get all non-internal actions that have been registered, along with their schemas.
pub fn action_schemas(
&self,
- generator: &mut schemars::r#gen::SchemaGenerator,
- ) -> Vec<(&'static str, Option<schemars::schema::Schema>)> {
+ generator: &mut schemars::SchemaGenerator,
+ ) -> Vec<(&'static str, Option<schemars::Schema>)> {
self.actions.action_schemas(generator)
}
@@ -1,9 +1,10 @@
use anyhow::{Context as _, bail};
-use schemars::{JsonSchema, SchemaGenerator, schema::Schema};
+use schemars::{JsonSchema, json_schema};
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{self, Visitor},
};
+use std::borrow::Cow;
use std::{
fmt::{self, Display, Formatter},
hash::{Hash, Hasher},
@@ -99,22 +100,14 @@ impl Visitor<'_> for RgbaVisitor {
}
impl JsonSchema for Rgba {
- fn schema_name() -> String {
- "Rgba".to_string()
+ fn schema_name() -> Cow<'static, str> {
+ "Rgba".into()
}
- fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
- use schemars::schema::{InstanceType, SchemaObject, StringValidation};
-
- Schema::Object(SchemaObject {
- instance_type: Some(InstanceType::String.into()),
- string: Some(Box::new(StringValidation {
- pattern: Some(
- r"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$".to_string(),
- ),
- ..Default::default()
- })),
- ..Default::default()
+ fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
+ json_schema!({
+ "type": "string",
+ "pattern": "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"
})
}
}
@@ -629,11 +622,11 @@ impl From<Rgba> for Hsla {
}
impl JsonSchema for Hsla {
- fn schema_name() -> String {
+ fn schema_name() -> Cow<'static, str> {
Rgba::schema_name()
}
- fn json_schema(generator: &mut SchemaGenerator) -> Schema {
+ fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
Rgba::json_schema(generator)
}
}
@@ -6,8 +6,9 @@ use anyhow::{Context as _, anyhow};
use core::fmt::Debug;
use derive_more::{Add, AddAssign, Div, DivAssign, Mul, Neg, Sub, SubAssign};
use refineable::Refineable;
-use schemars::{JsonSchema, SchemaGenerator, schema::Schema};
+use schemars::{JsonSchema, json_schema};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
+use std::borrow::Cow;
use std::{
cmp::{self, PartialOrd},
fmt::{self, Display},
@@ -3229,20 +3230,14 @@ impl TryFrom<&'_ str> for AbsoluteLength {
}
impl JsonSchema for AbsoluteLength {
- fn schema_name() -> String {
- "AbsoluteLength".to_string()
+ fn schema_name() -> Cow<'static, str> {
+ "AbsoluteLength".into()
}
- fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
- use schemars::schema::{InstanceType, SchemaObject, StringValidation};
-
- Schema::Object(SchemaObject {
- instance_type: Some(InstanceType::String.into()),
- string: Some(Box::new(StringValidation {
- pattern: Some(r"^-?\d+(\.\d+)?(px|rem)$".to_string()),
- ..Default::default()
- })),
- ..Default::default()
+ fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
+ json_schema!({
+ "type": "string",
+ "pattern": r"^-?\d+(\.\d+)?(px|rem)$"
})
}
}
@@ -3366,20 +3361,14 @@ impl TryFrom<&'_ str> for DefiniteLength {
}
impl JsonSchema for DefiniteLength {
- fn schema_name() -> String {
- "DefiniteLength".to_string()
+ fn schema_name() -> Cow<'static, str> {
+ "DefiniteLength".into()
}
- fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
- use schemars::schema::{InstanceType, SchemaObject, StringValidation};
-
- Schema::Object(SchemaObject {
- instance_type: Some(InstanceType::String.into()),
- string: Some(Box::new(StringValidation {
- pattern: Some(r"^-?\d+(\.\d+)?(px|rem|%)$".to_string()),
- ..Default::default()
- })),
- ..Default::default()
+ fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
+ json_schema!({
+ "type": "string",
+ "pattern": r"^-?\d+(\.\d+)?(px|rem|%)$"
})
}
}
@@ -3480,20 +3469,14 @@ impl TryFrom<&'_ str> for Length {
}
impl JsonSchema for Length {
- fn schema_name() -> String {
- "Length".to_string()
+ fn schema_name() -> Cow<'static, str> {
+ "Length".into()
}
- fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
- use schemars::schema::{InstanceType, SchemaObject, StringValidation};
-
- Schema::Object(SchemaObject {
- instance_type: Some(InstanceType::String.into()),
- string: Some(Box::new(StringValidation {
- pattern: Some(r"^(auto|-?\d+(\.\d+)?(px|rem|%))$".to_string()),
- ..Default::default()
- })),
- ..Default::default()
+ fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
+ json_schema!({
+ "type": "string",
+ "pattern": r"^(auto|-?\d+(\.\d+)?(px|rem|%))$"
})
}
}
@@ -2,7 +2,10 @@ use derive_more::{Deref, DerefMut};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
-use std::{borrow::Borrow, sync::Arc};
+use std::{
+ borrow::{Borrow, Cow},
+ sync::Arc,
+};
use util::arc_cow::ArcCow;
/// A shared string is an immutable string that can be cheaply cloned in GPUI
@@ -23,12 +26,16 @@ impl SharedString {
}
impl JsonSchema for SharedString {
- fn schema_name() -> String {
+ fn inline_schema() -> bool {
+ String::inline_schema()
+ }
+
+ fn schema_name() -> Cow<'static, str> {
String::schema_name()
}
- fn json_schema(r#gen: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
- String::json_schema(r#gen)
+ fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
+ String::json_schema(generator)
}
}
@@ -1,6 +1,7 @@
+use std::borrow::Cow;
use std::sync::Arc;
-use schemars::schema::{InstanceType, SchemaObject};
+use schemars::{JsonSchema, json_schema};
/// The OpenType features that can be configured for a given font.
#[derive(Default, Clone, Eq, PartialEq, Hash)]
@@ -128,36 +129,22 @@ impl serde::Serialize for FontFeatures {
}
}
-impl schemars::JsonSchema for FontFeatures {
- fn schema_name() -> String {
+impl JsonSchema for FontFeatures {
+ fn schema_name() -> Cow<'static, str> {
"FontFeatures".into()
}
- fn json_schema(_: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
- let mut schema = SchemaObject::default();
- schema.instance_type = Some(schemars::schema::SingleOrVec::Single(Box::new(
- InstanceType::Object,
- )));
- {
- let mut property = SchemaObject {
- instance_type: Some(schemars::schema::SingleOrVec::Vec(vec![
- InstanceType::Boolean,
- InstanceType::Integer,
- ])),
- ..Default::default()
- };
-
- {
- let mut number_constraints = property.number();
- number_constraints.multiple_of = Some(1.0);
- number_constraints.minimum = Some(0.0);
+ fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
+ json_schema!({
+ "type": "object",
+ "patternProperties": {
+ "[0-9a-zA-Z]{4}$": {
+ "type": ["boolean", "integer"],
+ "minimum": 0,
+ "multipleOf": 1
+ }
}
- schema
- .object()
- .pattern_properties
- .insert("[0-9a-zA-Z]{4}$".into(), property.into());
- }
- schema.into()
+ })
}
}
@@ -159,8 +159,8 @@ pub(crate) fn derive_action(input: TokenStream) -> TokenStream {
}
fn action_json_schema(
- _generator: &mut gpui::private::schemars::r#gen::SchemaGenerator,
- ) -> Option<gpui::private::schemars::schema::Schema> {
+ _generator: &mut gpui::private::schemars::SchemaGenerator,
+ ) -> Option<gpui::private::schemars::Schema> {
#json_schema_fn_body
}
@@ -967,6 +967,7 @@ fn toggle_show_inline_completions_for_language(
all_language_settings(None, cx).show_edit_predictions(Some(&language), cx);
update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
file.languages
+ .0
.entry(language.name())
.or_default()
.show_edit_predictions = Some(!show_edit_predictions);
@@ -39,6 +39,7 @@ globset.workspace = true
gpui.workspace = true
http_client.workspace = true
imara-diff.workspace = true
+inventory.workspace = true
itertools.workspace = true
log.workspace = true
lsp.workspace = true
@@ -2006,7 +2006,7 @@ fn test_autoindent_language_without_indents_query(cx: &mut App) {
#[gpui::test]
fn test_autoindent_with_injected_languages(cx: &mut App) {
init_settings(cx, |settings| {
- settings.languages.extend([
+ settings.languages.0.extend([
(
"HTML".into(),
LanguageSettingsContent {
@@ -39,11 +39,7 @@ use lsp::{CodeActionKind, InitializeParams, LanguageServerBinary, LanguageServer
pub use manifest::{ManifestDelegate, ManifestName, ManifestProvider, ManifestQuery};
use parking_lot::Mutex;
use regex::Regex;
-use schemars::{
- JsonSchema,
- r#gen::SchemaGenerator,
- schema::{InstanceType, Schema, SchemaObject},
-};
+use schemars::{JsonSchema, json_schema};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
use serde_json::Value;
use settings::WorktreeId;
@@ -694,7 +690,6 @@ pub struct LanguageConfig {
pub matcher: LanguageMatcher,
/// List of bracket types in a language.
#[serde(default)]
- #[schemars(schema_with = "bracket_pair_config_json_schema")]
pub brackets: BracketPairConfig,
/// If set to true, auto indentation uses last non empty line to determine
/// the indentation level for a new line.
@@ -944,10 +939,9 @@ fn deserialize_regex<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Regex>, D
}
}
-fn regex_json_schema(_: &mut SchemaGenerator) -> Schema {
- Schema::Object(SchemaObject {
- instance_type: Some(InstanceType::String.into()),
- ..Default::default()
+fn regex_json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
+ json_schema!({
+ "type": "string"
})
}
@@ -988,12 +982,12 @@ pub struct FakeLspAdapter {
/// This struct includes settings for defining which pairs of characters are considered brackets and
/// also specifies any language-specific scopes where these pairs should be ignored for bracket matching purposes.
#[derive(Clone, Debug, Default, JsonSchema)]
+#[schemars(with = "Vec::<BracketPairContent>")]
pub struct BracketPairConfig {
/// A list of character pairs that should be treated as brackets in the context of a given language.
pub pairs: Vec<BracketPair>,
/// A list of tree-sitter scopes for which a given bracket should not be active.
/// N-th entry in `[Self::disabled_scopes_by_bracket_ix]` contains a list of disabled scopes for an n-th entry in `[Self::pairs]`
- #[serde(skip)]
pub disabled_scopes_by_bracket_ix: Vec<Vec<String>>,
}
@@ -1003,10 +997,6 @@ impl BracketPairConfig {
}
}
-fn bracket_pair_config_json_schema(r#gen: &mut SchemaGenerator) -> Schema {
- Option::<Vec<BracketPairContent>>::json_schema(r#gen)
-}
-
#[derive(Deserialize, JsonSchema)]
pub struct BracketPairContent {
#[serde(flatten)]
@@ -1170,7 +1170,7 @@ impl LanguageRegistryState {
if let Some(theme) = self.theme.as_ref() {
language.set_theme(theme.syntax());
}
- self.language_settings.languages.insert(
+ self.language_settings.languages.0.insert(
language.name(),
LanguageSettingsContent {
tab_size: language.config.tab_size,
@@ -3,7 +3,6 @@
use crate::{File, Language, LanguageName, LanguageServerName};
use anyhow::Result;
use collections::{FxHashMap, HashMap, HashSet};
-use core::slice;
use ec4rs::{
Properties as EditorconfigProperties,
property::{FinalNewline, IndentSize, IndentStyle, TabWidth, TrimTrailingWs},
@@ -11,17 +10,15 @@ use ec4rs::{
use globset::{Glob, GlobMatcher, GlobSet, GlobSetBuilder};
use gpui::{App, Modifiers};
use itertools::{Either, Itertools};
-use schemars::{
- JsonSchema,
- schema::{InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec},
-};
+use schemars::{JsonSchema, json_schema};
use serde::{
Deserialize, Deserializer, Serialize,
de::{self, IntoDeserializer, MapAccess, SeqAccess, Visitor},
};
-use serde_json::Value;
+
use settings::{
- Settings, SettingsLocation, SettingsSources, SettingsStore, add_references_to_properties,
+ ParameterizedJsonSchema, Settings, SettingsLocation, SettingsSources, SettingsStore,
+ replace_subschema,
};
use shellexpand;
use std::{borrow::Cow, num::NonZeroU32, path::Path, sync::Arc};
@@ -306,13 +303,42 @@ pub struct AllLanguageSettingsContent {
pub defaults: LanguageSettingsContent,
/// The settings for individual languages.
#[serde(default)]
- pub languages: HashMap<LanguageName, LanguageSettingsContent>,
+ pub languages: LanguageToSettingsMap,
/// Settings for associating file extensions and filenames
/// with languages.
#[serde(default)]
pub file_types: HashMap<Arc<str>, Vec<String>>,
}
+/// Map from language name to settings. Its `ParameterizedJsonSchema` allows only known language
+/// names in the keys.
+#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
+pub struct LanguageToSettingsMap(pub HashMap<LanguageName, LanguageSettingsContent>);
+
+inventory::submit! {
+ ParameterizedJsonSchema {
+ add_and_get_ref: |generator, params, _cx| {
+ let language_settings_content_ref = generator
+ .subschema_for::<LanguageSettingsContent>()
+ .to_value();
+ let schema = json_schema!({
+ "type": "object",
+ "properties": params
+ .language_names
+ .iter()
+ .map(|name| {
+ (
+ name.clone(),
+ language_settings_content_ref.clone(),
+ )
+ })
+ .collect::<serde_json::Map<_, _>>()
+ });
+ replace_subschema::<LanguageToSettingsMap>(generator, schema)
+ }
+ }
+}
+
/// Controls how completions are processed for this language.
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
@@ -648,45 +674,30 @@ pub enum FormatOnSave {
On,
/// Files should not be formatted on save.
Off,
- List(FormatterList),
+ List(Vec<Formatter>),
}
impl JsonSchema for FormatOnSave {
- fn schema_name() -> String {
+ fn schema_name() -> Cow<'static, str> {
"OnSaveFormatter".into()
}
- fn json_schema(generator: &mut schemars::r#gen::SchemaGenerator) -> Schema {
- let mut schema = SchemaObject::default();
+ fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
let formatter_schema = Formatter::json_schema(generator);
- schema.instance_type = Some(
- vec![
- InstanceType::Object,
- InstanceType::String,
- InstanceType::Array,
- ]
- .into(),
- );
-
- let valid_raw_values = SchemaObject {
- enum_values: Some(vec![
- Value::String("on".into()),
- Value::String("off".into()),
- Value::String("prettier".into()),
- Value::String("language_server".into()),
- ]),
- ..Default::default()
- };
- let mut nested_values = SchemaObject::default();
- nested_values.array().items = Some(formatter_schema.clone().into());
-
- schema.subschemas().any_of = Some(vec![
- nested_values.into(),
- valid_raw_values.into(),
- formatter_schema,
- ]);
- schema.into()
+ json_schema!({
+ "oneOf": [
+ {
+ "type": "array",
+ "items": formatter_schema
+ },
+ {
+ "type": "string",
+ "enum": ["on", "off", "prettier", "language_server"]
+ },
+ formatter_schema
+ ]
+ })
}
}
@@ -725,11 +736,11 @@ impl<'de> Deserialize<'de> for FormatOnSave {
} else if v == "off" {
Ok(Self::Value::Off)
} else if v == "language_server" {
- Ok(Self::Value::List(FormatterList(
- Formatter::LanguageServer { name: None }.into(),
- )))
+ Ok(Self::Value::List(vec![Formatter::LanguageServer {
+ name: None,
+ }]))
} else {
- let ret: Result<FormatterList, _> =
+ let ret: Result<Vec<Formatter>, _> =
Deserialize::deserialize(v.into_deserializer());
ret.map(Self::Value::List)
}
@@ -738,7 +749,7 @@ impl<'de> Deserialize<'de> for FormatOnSave {
where
A: MapAccess<'d>,
{
- let ret: Result<FormatterList, _> =
+ let ret: Result<Vec<Formatter>, _> =
Deserialize::deserialize(de::value::MapAccessDeserializer::new(map));
ret.map(Self::Value::List)
}
@@ -746,7 +757,7 @@ impl<'de> Deserialize<'de> for FormatOnSave {
where
A: SeqAccess<'d>,
{
- let ret: Result<FormatterList, _> =
+ let ret: Result<Vec<Formatter>, _> =
Deserialize::deserialize(de::value::SeqAccessDeserializer::new(map));
ret.map(Self::Value::List)
}
@@ -783,45 +794,30 @@ pub enum SelectedFormatter {
/// or falling back to formatting via language server.
#[default]
Auto,
- List(FormatterList),
+ List(Vec<Formatter>),
}
impl JsonSchema for SelectedFormatter {
- fn schema_name() -> String {
+ fn schema_name() -> Cow<'static, str> {
"Formatter".into()
}
- fn json_schema(generator: &mut schemars::r#gen::SchemaGenerator) -> Schema {
- let mut schema = SchemaObject::default();
+ fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
let formatter_schema = Formatter::json_schema(generator);
- schema.instance_type = Some(
- vec![
- InstanceType::Object,
- InstanceType::String,
- InstanceType::Array,
- ]
- .into(),
- );
-
- let valid_raw_values = SchemaObject {
- enum_values: Some(vec![
- Value::String("auto".into()),
- Value::String("prettier".into()),
- Value::String("language_server".into()),
- ]),
- ..Default::default()
- };
-
- let mut nested_values = SchemaObject::default();
- nested_values.array().items = Some(formatter_schema.clone().into());
-
- schema.subschemas().any_of = Some(vec![
- nested_values.into(),
- valid_raw_values.into(),
- formatter_schema,
- ]);
- schema.into()
+ json_schema!({
+ "oneOf": [
+ {
+ "type": "array",
+ "items": formatter_schema
+ },
+ {
+ "type": "string",
+ "enum": ["auto", "prettier", "language_server"]
+ },
+ formatter_schema
+ ]
+ })
}
}
@@ -836,6 +832,7 @@ impl Serialize for SelectedFormatter {
}
}
}
+
impl<'de> Deserialize<'de> for SelectedFormatter {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
@@ -856,11 +853,11 @@ impl<'de> Deserialize<'de> for SelectedFormatter {
if v == "auto" {
Ok(Self::Value::Auto)
} else if v == "language_server" {
- Ok(Self::Value::List(FormatterList(
- Formatter::LanguageServer { name: None }.into(),
- )))
+ Ok(Self::Value::List(vec![Formatter::LanguageServer {
+ name: None,
+ }]))
} else {
- let ret: Result<FormatterList, _> =
+ let ret: Result<Vec<Formatter>, _> =
Deserialize::deserialize(v.into_deserializer());
ret.map(SelectedFormatter::List)
}
@@ -869,7 +866,7 @@ impl<'de> Deserialize<'de> for SelectedFormatter {
where
A: MapAccess<'d>,
{
- let ret: Result<FormatterList, _> =
+ let ret: Result<Vec<Formatter>, _> =
Deserialize::deserialize(de::value::MapAccessDeserializer::new(map));
ret.map(SelectedFormatter::List)
}
@@ -877,7 +874,7 @@ impl<'de> Deserialize<'de> for SelectedFormatter {
where
A: SeqAccess<'d>,
{
- let ret: Result<FormatterList, _> =
+ let ret: Result<Vec<Formatter>, _> =
Deserialize::deserialize(de::value::SeqAccessDeserializer::new(map));
ret.map(SelectedFormatter::List)
}
@@ -885,19 +882,6 @@ impl<'de> Deserialize<'de> for SelectedFormatter {
deserializer.deserialize_any(FormatDeserializer)
}
}
-/// Controls which formatter should be used when formatting code.
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
-#[serde(rename_all = "snake_case", transparent)]
-pub struct FormatterList(pub SingleOrVec<Formatter>);
-
-impl AsRef<[Formatter]> for FormatterList {
- fn as_ref(&self) -> &[Formatter] {
- match &self.0 {
- SingleOrVec::Single(single) => slice::from_ref(single),
- SingleOrVec::Vec(v) => v,
- }
- }
-}
/// Controls which formatter should be used when formatting code. If there are multiple formatters, they are executed in the order of declaration.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
@@ -1209,7 +1193,7 @@ impl settings::Settings for AllLanguageSettings {
serde_json::from_value(serde_json::to_value(&default_value.defaults)?)?;
let mut languages = HashMap::default();
- for (language_name, settings) in &default_value.languages {
+ for (language_name, settings) in &default_value.languages.0 {
let mut language_settings = defaults.clone();
merge_settings(&mut language_settings, settings);
languages.insert(language_name.clone(), language_settings);
@@ -1310,7 +1294,7 @@ impl settings::Settings for AllLanguageSettings {
}
// A user's language-specific settings override default language-specific settings.
- for (language_name, user_language_settings) in &user_settings.languages {
+ for (language_name, user_language_settings) in &user_settings.languages.0 {
merge_settings(
languages
.entry(language_name.clone())
@@ -1366,51 +1350,6 @@ impl settings::Settings for AllLanguageSettings {
})
}
- fn json_schema(
- generator: &mut schemars::r#gen::SchemaGenerator,
- params: &settings::SettingsJsonSchemaParams,
- _: &App,
- ) -> 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 languages.
- 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())]);
-
- add_references_to_properties(
- &mut root_schema,
- &[("languages", "#/definitions/Languages")],
- );
-
- root_schema
- }
-
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
let d = &mut current.defaults;
if let Some(size) = vscode
@@ -1674,29 +1613,26 @@ mod tests {
let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
assert_eq!(
settings.formatter,
- Some(SelectedFormatter::List(FormatterList(
- Formatter::LanguageServer { name: None }.into()
- )))
+ Some(SelectedFormatter::List(vec![Formatter::LanguageServer {
+ name: None
+ }]))
);
let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}]}";
let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
assert_eq!(
settings.formatter,
- Some(SelectedFormatter::List(FormatterList(
- vec![Formatter::LanguageServer { name: None }].into()
- )))
+ Some(SelectedFormatter::List(vec![Formatter::LanguageServer {
+ name: None
+ }]))
);
let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}, \"prettier\"]}";
let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
assert_eq!(
settings.formatter,
- Some(SelectedFormatter::List(FormatterList(
- vec![
- Formatter::LanguageServer { name: None },
- Formatter::Prettier
- ]
- .into()
- )))
+ Some(SelectedFormatter::List(vec![
+ Formatter::LanguageServer { name: None },
+ Formatter::Prettier
+ ]))
);
}
@@ -269,10 +269,9 @@ impl JsonLspAdapter {
#[cfg(debug_assertions)]
fn generate_inspector_style_schema() -> serde_json_lenient::Value {
- let schema = schemars::r#gen::SchemaSettings::draft07()
- .with(|settings| settings.option_add_null_type = false)
+ let schema = schemars::generate::SchemaSettings::draft07()
.into_generator()
- .into_root_schema_for::<gpui::StyleRefinement>();
+ .root_schema_for::<gpui::StyleRefinement>();
serde_json_lenient::to_value(schema).unwrap()
}
@@ -15,11 +15,7 @@ use gpui::{App, AppContext as _, AsyncApp, BackgroundExecutor, SharedString, Tas
use notification::DidChangeWorkspaceFolders;
use parking_lot::{Mutex, RwLock};
use postage::{barrier, prelude::Stream};
-use schemars::{
- JsonSchema,
- r#gen::SchemaGenerator,
- schema::{InstanceType, Schema, SchemaObject},
-};
+use schemars::JsonSchema;
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use serde_json::{Value, json, value::RawValue};
use smol::{
@@ -130,7 +126,10 @@ impl LanguageServerId {
}
/// A name of a language server.
-#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
+#[derive(
+ Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize, JsonSchema,
+)]
+#[serde(transparent)]
pub struct LanguageServerName(pub SharedString);
impl std::fmt::Display for LanguageServerName {
@@ -151,20 +150,6 @@ impl AsRef<OsStr> for LanguageServerName {
}
}
-impl JsonSchema for LanguageServerName {
- fn schema_name() -> String {
- "LanguageServerName".into()
- }
-
- fn json_schema(_: &mut SchemaGenerator) -> Schema {
- SchemaObject {
- instance_type: Some(InstanceType::String.into()),
- ..Default::default()
- }
- .into()
- }
-}
-
impl LanguageServerName {
pub const fn new_static(s: &'static str) -> Self {
Self(SharedString::new_static(s))
@@ -1405,7 +1405,7 @@ impl LocalLspStore {
let formatters = match (trigger, &settings.format_on_save) {
(FormatTrigger::Save, FormatOnSave::Off) => &[],
- (FormatTrigger::Save, FormatOnSave::List(formatters)) => formatters.as_ref(),
+ (FormatTrigger::Save, FormatOnSave::List(formatters)) => formatters.as_slice(),
(FormatTrigger::Manual, _) | (FormatTrigger::Save, FormatOnSave::On) => {
match &settings.formatter {
SelectedFormatter::Auto => {
@@ -1417,7 +1417,7 @@ impl LocalLspStore {
std::slice::from_ref(&Formatter::LanguageServer { name: None })
}
}
- SelectedFormatter::List(formatter_list) => formatter_list.as_ref(),
+ SelectedFormatter::List(formatter_list) => formatter_list.as_slice(),
}
}
};
@@ -705,7 +705,6 @@ pub fn prettier_plugins_for_language(
SelectedFormatter::Auto => Some(&language_settings.prettier.plugins),
SelectedFormatter::List(list) => list
- .as_ref()
.contains(&Formatter::Prettier)
.then_some(&language_settings.prettier.plugins),
}
@@ -2023,7 +2023,7 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) {
cx.update(|cx| {
SettingsStore::update_global(cx, |settings, cx| {
settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
- settings.languages.insert(
+ settings.languages.0.insert(
"Rust".into(),
LanguageSettingsContent {
enable_language_server: Some(false),
@@ -2042,14 +2042,14 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) {
cx.update(|cx| {
SettingsStore::update_global(cx, |settings, cx| {
settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
- settings.languages.insert(
+ settings.languages.0.insert(
LanguageName::new("Rust"),
LanguageSettingsContent {
enable_language_server: Some(true),
..Default::default()
},
);
- settings.languages.insert(
+ settings.languages.0.insert(
LanguageName::new("JavaScript"),
LanguageSettingsContent {
enable_language_server: Some(false),
@@ -5,13 +5,10 @@ use gpui::{
Action, ActionBuildError, App, InvalidKeystrokeError, KEYSTROKE_PARSE_EXPECTED_MESSAGE,
KeyBinding, KeyBindingContextPredicate, KeyBindingMetaIndex, NoAction,
};
-use schemars::{
- JsonSchema,
- r#gen::{SchemaGenerator, SchemaSettings},
- schema::{ArrayValidation, InstanceType, Schema, SchemaObject, SubschemaValidation},
-};
+use schemars::{JsonSchema, json_schema};
use serde::Deserialize;
-use serde_json::Value;
+use serde_json::{Value, json};
+use std::borrow::Cow;
use std::{any::TypeId, fmt::Write, rc::Rc, sync::Arc, sync::LazyLock};
use util::{
asset_str,
@@ -123,14 +120,14 @@ impl std::fmt::Display for KeymapAction {
impl JsonSchema for KeymapAction {
/// This is used when generating the JSON schema for the `KeymapAction` type, so that it can
/// reference the keymap action schema.
- fn schema_name() -> String {
+ fn schema_name() -> Cow<'static, str> {
"KeymapAction".into()
}
/// This schema will be replaced with the full action schema in
/// `KeymapFile::generate_json_schema`.
- fn json_schema(_: &mut SchemaGenerator) -> Schema {
- Schema::Bool(true)
+ fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
+ json_schema!(true)
}
}
@@ -424,9 +421,7 @@ impl KeymapFile {
}
pub fn generate_json_schema_for_registered_actions(cx: &mut App) -> Value {
- let mut generator = SchemaSettings::draft07()
- .with(|settings| settings.option_add_null_type = false)
- .into_generator();
+ let mut generator = schemars::generate::SchemaSettings::draft07().into_generator();
let action_schemas = cx.action_schemas(&mut generator);
let deprecations = cx.deprecated_actions_to_preferred_actions();
@@ -440,92 +435,70 @@ impl KeymapFile {
}
fn generate_json_schema(
- generator: SchemaGenerator,
- action_schemas: Vec<(&'static str, Option<Schema>)>,
+ mut generator: schemars::SchemaGenerator,
+ action_schemas: Vec<(&'static str, Option<schemars::Schema>)>,
deprecations: &HashMap<&'static str, &'static str>,
deprecation_messages: &HashMap<&'static str, &'static str>,
) -> serde_json::Value {
- fn set<I, O>(input: I) -> Option<O>
- where
- I: Into<O>,
- {
- Some(input.into())
- }
-
- fn add_deprecation(schema_object: &mut SchemaObject, message: String) {
- schema_object.extensions.insert(
- // deprecationMessage is not part of the JSON Schema spec,
- // but json-language-server recognizes it.
- "deprecationMessage".to_owned(),
+ fn add_deprecation(schema: &mut schemars::Schema, message: String) {
+ schema.insert(
+ // deprecationMessage is not part of the JSON Schema spec, but
+ // json-language-server recognizes it.
+ "deprecationMessage".to_string(),
Value::String(message),
);
}
- fn add_deprecation_preferred_name(schema_object: &mut SchemaObject, new_name: &str) {
- add_deprecation(schema_object, format!("Deprecated, use {new_name}"));
+ fn add_deprecation_preferred_name(schema: &mut schemars::Schema, new_name: &str) {
+ add_deprecation(schema, format!("Deprecated, use {new_name}"));
}
- fn add_description(schema_object: &mut SchemaObject, description: String) {
- schema_object
- .metadata
- .get_or_insert(Default::default())
- .description = Some(description);
+ fn add_description(schema: &mut schemars::Schema, description: String) {
+ schema.insert("description".to_string(), Value::String(description));
}
- let empty_object: SchemaObject = SchemaObject {
- instance_type: set(InstanceType::Object),
- ..Default::default()
- };
+ let empty_object = json_schema!({
+ "type": "object"
+ });
// This is a workaround for a json-language-server issue where it matches the first
// alternative that matches the value's shape and uses that for documentation.
//
// In the case of the array validations, it would even provide an error saying that the name
// must match the name of the first alternative.
- let mut plain_action = SchemaObject {
- instance_type: set(InstanceType::String),
- const_value: Some(Value::String("".to_owned())),
- ..Default::default()
- };
+ let mut plain_action = json_schema!({
+ "type": "string",
+ "const": ""
+ });
let no_action_message = "No action named this.";
add_description(&mut plain_action, no_action_message.to_owned());
add_deprecation(&mut plain_action, no_action_message.to_owned());
- let mut matches_action_name = SchemaObject {
- const_value: Some(Value::String("".to_owned())),
- ..Default::default()
- };
- let no_action_message = "No action named this that takes input.";
- add_description(&mut matches_action_name, no_action_message.to_owned());
- add_deprecation(&mut matches_action_name, no_action_message.to_owned());
- let action_with_input = SchemaObject {
- instance_type: set(InstanceType::Array),
- array: set(ArrayValidation {
- items: set(vec![
- matches_action_name.into(),
- // Accept any value, as we want this to be the preferred match when there is a
- // typo in the name.
- Schema::Bool(true),
- ]),
- min_items: Some(2),
- max_items: Some(2),
- ..Default::default()
- }),
- ..Default::default()
- };
- let mut keymap_action_alternatives = vec![plain_action.into(), action_with_input.into()];
- for (name, action_schema) in action_schemas.into_iter() {
- let schema = if let Some(Schema::Object(schema)) = action_schema {
- Some(schema)
- } else {
- None
- };
+ let mut matches_action_name = json_schema!({
+ "const": ""
+ });
+ let no_action_message_input = "No action named this that takes input.";
+ add_description(&mut matches_action_name, no_action_message_input.to_owned());
+ add_deprecation(&mut matches_action_name, no_action_message_input.to_owned());
+
+ let action_with_input = json_schema!({
+ "type": "array",
+ "items": [
+ matches_action_name,
+ true
+ ],
+ "minItems": 2,
+ "maxItems": 2
+ });
+ let mut keymap_action_alternatives = vec![plain_action, action_with_input];
- let description = schema.as_ref().and_then(|schema| {
+ for (name, action_schema) in action_schemas.into_iter() {
+ let description = action_schema.as_ref().and_then(|schema| {
schema
- .metadata
- .as_ref()
- .and_then(|metadata| metadata.description.clone())
+ .as_object()
+ .and_then(|obj| obj.get("description"))
+ .and_then(|v| v.as_str())
+ .map(|s| s.to_string())
});
let deprecation = if name == NoAction.name() {
@@ -535,84 +508,64 @@ impl KeymapFile {
};
// Add an alternative for plain action names.
- let mut plain_action = SchemaObject {
- instance_type: set(InstanceType::String),
- const_value: Some(Value::String(name.to_string())),
- ..Default::default()
- };
+ let mut plain_action = json_schema!({
+ "type": "string",
+ "const": name
+ });
if let Some(message) = deprecation_messages.get(name) {
add_deprecation(&mut plain_action, message.to_string());
} else if let Some(new_name) = deprecation {
add_deprecation_preferred_name(&mut plain_action, new_name);
}
- if let Some(description) = description.clone() {
- add_description(&mut plain_action, description);
+ if let Some(desc) = description.clone() {
+ add_description(&mut plain_action, desc);
}
- keymap_action_alternatives.push(plain_action.into());
+ keymap_action_alternatives.push(plain_action);
// Add an alternative for actions with data specified as a [name, data] array.
//
- // When a struct with no deserializable fields is added with impl_actions! /
- // impl_actions_as! an empty object schema is produced. The action should be invoked
- // without data in this case.
- if let Some(schema) = schema {
+ // When a struct with no deserializable fields is added by deriving `Action`, an empty
+ // object schema is produced. The action should be invoked without data in this case.
+ if let Some(schema) = action_schema {
if schema != empty_object {
- let mut matches_action_name = SchemaObject {
- const_value: Some(Value::String(name.to_string())),
- ..Default::default()
- };
- if let Some(description) = description.clone() {
- add_description(&mut matches_action_name, description);
+ let mut matches_action_name = json_schema!({
+ "const": name
+ });
+ if let Some(desc) = description.clone() {
+ add_description(&mut matches_action_name, desc);
}
if let Some(message) = deprecation_messages.get(name) {
add_deprecation(&mut matches_action_name, message.to_string());
} else if let Some(new_name) = deprecation {
add_deprecation_preferred_name(&mut matches_action_name, new_name);
}
- let action_with_input = SchemaObject {
- instance_type: set(InstanceType::Array),
- array: set(ArrayValidation {
- items: set(vec![matches_action_name.into(), schema.into()]),
- min_items: Some(2),
- max_items: Some(2),
- ..Default::default()
- }),
- ..Default::default()
- };
- keymap_action_alternatives.push(action_with_input.into());
+ let action_with_input = json_schema!({
+ "type": "array",
+ "items": [matches_action_name, schema],
+ "minItems": 2,
+ "maxItems": 2
+ });
+ keymap_action_alternatives.push(action_with_input);
}
}
}
// Placing null first causes json-language-server to default assuming actions should be
// null, so place it last.
- keymap_action_alternatives.push(
- SchemaObject {
- instance_type: set(InstanceType::Null),
- ..Default::default()
- }
- .into(),
- );
+ keymap_action_alternatives.push(json_schema!({
+ "type": "null"
+ }));
- let action_schema = SchemaObject {
- subschemas: set(SubschemaValidation {
- one_of: Some(keymap_action_alternatives),
- ..Default::default()
+ // The `KeymapSection` schema will reference the `KeymapAction` schema by name, so setting
+ // the definition of `KeymapAction` results in the full action schema being used.
+ generator.definitions_mut().insert(
+ KeymapAction::schema_name().to_string(),
+ json!({
+ "oneOf": keymap_action_alternatives
}),
- ..Default::default()
- }
- .into();
+ );
- // The `KeymapSection` schema will reference the `KeymapAction` schema by name, so replacing
- // the definition of `KeymapAction` results in the full action schema being used.
- let mut root_schema = generator.into_root_schema_for::<KeymapFile>();
- root_schema
- .definitions
- .insert(KeymapAction::schema_name(), action_schema);
-
- // This and other json schemas can be viewed via `dev: open language server logs` ->
- // `json-language-server` -> `Server Info`.
- serde_json::to_value(root_schema).unwrap()
+ generator.root_schema_for::<KeymapFile>().to_value()
}
pub fn sections(&self) -> impl DoubleEndedIterator<Item = &KeymapSection> {
@@ -1,11 +1,9 @@
-use std::{ops::Range, sync::LazyLock};
-
use anyhow::Result;
-use schemars::schema::{
- ArrayValidation, InstanceType, RootSchema, Schema, SchemaObject, SingleOrVec,
-};
+use gpui::App;
+use schemars::{JsonSchema, Schema};
use serde::{Serialize, de::DeserializeOwned};
use serde_json::Value;
+use std::{ops::Range, sync::LazyLock};
use tree_sitter::{Query, StreamingIterator as _};
use util::RangeExt;
@@ -14,70 +12,43 @@ pub struct SettingsJsonSchemaParams<'a> {
pub font_names: &'a [String],
}
-impl SettingsJsonSchemaParams<'_> {
- pub fn font_family_schema(&self) -> Schema {
- let available_fonts: Vec<_> = self.font_names.iter().cloned().map(Value::String).collect();
-
- SchemaObject {
- instance_type: Some(InstanceType::String.into()),
- enum_values: Some(available_fonts),
- ..Default::default()
- }
- .into()
- }
-
- pub fn font_fallback_schema(&self) -> Schema {
- SchemaObject {
- instance_type: Some(SingleOrVec::Vec(vec![
- InstanceType::Array,
- InstanceType::Null,
- ])),
- array: Some(Box::new(ArrayValidation {
- items: Some(schemars::schema::SingleOrVec::Single(Box::new(
- self.font_family_schema(),
- ))),
- unique_items: Some(true),
- ..Default::default()
- })),
- ..Default::default()
- }
- .into()
- }
+pub struct ParameterizedJsonSchema {
+ pub add_and_get_ref:
+ fn(&mut schemars::SchemaGenerator, &SettingsJsonSchemaParams, &App) -> schemars::Schema,
}
-type PropertyName<'a> = &'a str;
-type ReferencePath<'a> = &'a str;
-
-/// Modifies the provided [`RootSchema`] by adding references to all of the specified properties.
-///
-/// # Examples
-///
-/// ```
-/// # let root_schema = RootSchema::default();
-/// add_references_to_properties(&mut root_schema, &[
-/// ("property_a", "#/definitions/DefinitionA"),
-/// ("property_b", "#/definitions/DefinitionB"),
-/// ])
-/// ```
-pub fn add_references_to_properties(
- root_schema: &mut RootSchema,
- properties_with_references: &[(PropertyName, ReferencePath)],
-) {
- for (property, definition) in properties_with_references {
- let Some(schema) = root_schema.schema.object().properties.get_mut(*property) else {
- log::warn!("property '{property}' not found in JSON schema");
- continue;
- };
-
- match schema {
- Schema::Object(schema) => {
- schema.reference = Some(definition.to_string());
- }
- Schema::Bool(_) => {
- // Boolean schemas can't have references.
- }
+inventory::collect!(ParameterizedJsonSchema);
+
+pub fn replace_subschema<T: JsonSchema>(
+ generator: &mut schemars::SchemaGenerator,
+ schema: schemars::Schema,
+) -> schemars::Schema {
+ const DEFINITIONS_PATH: &str = "#/definitions/";
+ // The key in definitions may not match T::schema_name() if multiple types have the same name.
+ // This is a workaround for there being no straightforward way to get the key used for a type -
+ // see https://github.com/GREsau/schemars/issues/449
+ let ref_schema = generator.subschema_for::<T>();
+ if let Some(serde_json::Value::String(definition_pointer)) = ref_schema.get("$ref") {
+ if let Some(definition_name) = definition_pointer.strip_prefix(DEFINITIONS_PATH) {
+ generator
+ .definitions_mut()
+ .insert(definition_name.to_string(), schema.to_value());
+ return ref_schema;
+ } else {
+ log::error!(
+ "bug: expected `$ref` field to start with {DEFINITIONS_PATH}, \
+ got {definition_pointer}"
+ );
}
+ } else {
+ log::error!("bug: expected `$ref` field in result of `subschema_for`");
}
+ // fallback on just using the schema name, which could collide.
+ let schema_name = T::schema_name();
+ generator
+ .definitions_mut()
+ .insert(schema_name.to_string(), schema.to_value());
+ Schema::new_ref(format!("{DEFINITIONS_PATH}{schema_name}"))
}
pub fn update_value_in_json_text<'a>(
@@ -6,7 +6,7 @@ use futures::{FutureExt, StreamExt, channel::mpsc, future::LocalBoxFuture};
use gpui::{App, AsyncApp, BorrowAppContext, Global, Task, UpdateGlobal};
use paths::{EDITORCONFIG_NAME, local_settings_file_relative_path, task_file_name};
-use schemars::{JsonSchema, r#gen::SchemaGenerator, schema::RootSchema};
+use schemars::{JsonSchema, json_schema};
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use serde_json::{Value, json};
use smallvec::SmallVec;
@@ -24,8 +24,8 @@ use util::{ResultExt as _, merge_non_null_json_value_into};
pub type EditorconfigProperties = ec4rs::Properties;
use crate::{
- SettingsJsonSchemaParams, VsCodeSettings, WorktreeId, parse_json_with_comments,
- update_value_in_json_text,
+ ParameterizedJsonSchema, SettingsJsonSchemaParams, VsCodeSettings, WorktreeId,
+ parse_json_with_comments, update_value_in_json_text,
};
/// A value that can be defined as a user setting.
@@ -57,14 +57,6 @@ pub trait Settings: 'static + Send + Sync {
where
Self: Sized;
- fn json_schema(
- generator: &mut SchemaGenerator,
- _: &SettingsJsonSchemaParams,
- _: &App,
- ) -> RootSchema {
- generator.root_schema_for::<Self::FileContent>()
- }
-
fn missing_default() -> anyhow::Error {
anyhow::anyhow!("missing default")
}
@@ -253,12 +245,7 @@ trait AnySettingValue: 'static + Send + Sync {
fn all_local_values(&self) -> Vec<(WorktreeId, Arc<Path>, &dyn Any)>;
fn set_global_value(&mut self, value: Box<dyn Any>);
fn set_local_value(&mut self, root_id: WorktreeId, path: Arc<Path>, value: Box<dyn Any>);
- fn json_schema(
- &self,
- generator: &mut SchemaGenerator,
- _: &SettingsJsonSchemaParams,
- cx: &App,
- ) -> RootSchema;
+ fn json_schema(&self, generator: &mut schemars::SchemaGenerator) -> schemars::Schema;
fn edits_for_update(
&self,
raw_settings: &serde_json::Value,
@@ -276,11 +263,11 @@ impl SettingsStore {
let (setting_file_updates_tx, mut setting_file_updates_rx) = mpsc::unbounded();
Self {
setting_values: Default::default(),
- raw_default_settings: serde_json::json!({}),
+ raw_default_settings: json!({}),
raw_global_settings: None,
- raw_user_settings: serde_json::json!({}),
+ raw_user_settings: json!({}),
raw_server_settings: None,
- raw_extension_settings: serde_json::json!({}),
+ raw_extension_settings: json!({}),
raw_local_settings: Default::default(),
raw_editorconfig_settings: BTreeMap::default(),
tab_size_callback: Default::default(),
@@ -877,108 +864,151 @@ impl SettingsStore {
}
pub fn json_schema(&self, schema_params: &SettingsJsonSchemaParams, cx: &App) -> Value {
- use schemars::{
- r#gen::SchemaSettings,
- schema::{Schema, SchemaObject},
- };
-
- let settings = SchemaSettings::draft07().with(|settings| {
- settings.option_add_null_type = true;
+ let mut generator = schemars::generate::SchemaSettings::draft07().into_generator();
+ let mut combined_schema = json!({
+ "type": "object",
+ "properties": {}
});
- let mut generator = SchemaGenerator::new(settings);
- let mut combined_schema = RootSchema::default();
+ // Merge together settings schemas, similarly to json schema's "allOf". This merging is
+ // recursive, though at time of writing this recursive nature isn't used very much. An
+ // example of it is the schema for `jupyter` having contribution from both `EditorSettings`
+ // and `JupyterSettings`.
+ //
+ // This logic could be removed in favor of "allOf", but then there isn't the opportunity to
+ // validate and fully control the merge.
for setting_value in self.setting_values.values() {
- let setting_schema = setting_value.json_schema(&mut generator, schema_params, cx);
- combined_schema
- .definitions
- .extend(setting_schema.definitions);
-
- let target_schema = if let Some(key) = setting_value.key() {
- let key_schema = combined_schema
- .schema
- .object()
- .properties
- .entry(key.to_string())
- .or_insert_with(|| Schema::Object(SchemaObject::default()));
- if let Schema::Object(key_schema) = key_schema {
- key_schema
- } else {
- continue;
+ let mut setting_schema = setting_value.json_schema(&mut generator);
+
+ if let Some(key) = setting_value.key() {
+ if let Some(properties) = combined_schema.get_mut("properties") {
+ if let Some(properties_obj) = properties.as_object_mut() {
+ if let Some(target) = properties_obj.get_mut(key) {
+ merge_schema(target, setting_schema.to_value());
+ } else {
+ properties_obj.insert(key.to_string(), setting_schema.to_value());
+ }
+ }
}
} else {
- &mut combined_schema.schema
- };
-
- merge_schema(target_schema, setting_schema.schema);
+ setting_schema.remove("description");
+ setting_schema.remove("additionalProperties");
+ merge_schema(&mut combined_schema, setting_schema.to_value());
+ }
}
- fn merge_schema(target: &mut SchemaObject, mut source: SchemaObject) {
- let source_subschemas = source.subschemas();
- let target_subschemas = target.subschemas();
- if let Some(all_of) = source_subschemas.all_of.take() {
- target_subschemas
- .all_of
- .get_or_insert(Vec::new())
- .extend(all_of);
- }
- if let Some(any_of) = source_subschemas.any_of.take() {
- target_subschemas
- .any_of
- .get_or_insert(Vec::new())
- .extend(any_of);
- }
- if let Some(one_of) = source_subschemas.one_of.take() {
- target_subschemas
- .one_of
- .get_or_insert(Vec::new())
- .extend(one_of);
- }
+ fn merge_schema(target: &mut serde_json::Value, source: serde_json::Value) {
+ let (Some(target_obj), serde_json::Value::Object(source_obj)) =
+ (target.as_object_mut(), source)
+ else {
+ return;
+ };
- if let Some(source) = source.object {
- let target_properties = &mut target.object().properties;
- for (key, value) in source.properties {
- match target_properties.entry(key) {
- btree_map::Entry::Vacant(e) => {
- e.insert(value);
+ for (source_key, source_value) in source_obj {
+ match source_key.as_str() {
+ "properties" => {
+ let serde_json::Value::Object(source_properties) = source_value else {
+ log::error!(
+ "bug: expected object for `{}` json schema field, but got: {}",
+ source_key,
+ source_value
+ );
+ continue;
+ };
+ let target_properties =
+ target_obj.entry(source_key.clone()).or_insert(json!({}));
+ let Some(target_properties) = target_properties.as_object_mut() else {
+ log::error!(
+ "bug: expected object for `{}` json schema field, but got: {}",
+ source_key,
+ target_properties
+ );
+ continue;
+ };
+ for (key, value) in source_properties {
+ if let Some(existing) = target_properties.get_mut(&key) {
+ merge_schema(existing, value);
+ } else {
+ target_properties.insert(key, value);
+ }
}
- btree_map::Entry::Occupied(e) => {
- if let (Schema::Object(target), Schema::Object(src)) =
- (e.into_mut(), value)
- {
- merge_schema(target, src);
+ }
+ "allOf" | "anyOf" | "oneOf" => {
+ let serde_json::Value::Array(source_array) = source_value else {
+ log::error!(
+ "bug: expected array for `{}` json schema field, but got: {}",
+ source_key,
+ source_value,
+ );
+ continue;
+ };
+ let target_array =
+ target_obj.entry(source_key.clone()).or_insert(json!([]));
+ let Some(target_array) = target_array.as_array_mut() else {
+ log::error!(
+ "bug: expected array for `{}` json schema field, but got: {}",
+ source_key,
+ target_array,
+ );
+ continue;
+ };
+ target_array.extend(source_array);
+ }
+ "type"
+ | "$ref"
+ | "enum"
+ | "minimum"
+ | "maximum"
+ | "pattern"
+ | "description"
+ | "additionalProperties" => {
+ if let Some(old_value) =
+ target_obj.insert(source_key.clone(), source_value.clone())
+ {
+ if old_value != source_value {
+ log::error!(
+ "bug: while merging JSON schemas, \
+ mismatch `\"{}\": {}` (before was `{}`)",
+ source_key,
+ old_value,
+ source_value
+ );
}
}
}
+ _ => {
+ log::error!(
+ "bug: while merging settings JSON schemas, \
+ encountered unexpected `\"{}\": {}`",
+ source_key,
+ source_value
+ );
+ }
}
}
+ }
- overwrite(&mut target.instance_type, source.instance_type);
- overwrite(&mut target.string, source.string);
- overwrite(&mut target.number, source.number);
- overwrite(&mut target.reference, source.reference);
- overwrite(&mut target.array, source.array);
- overwrite(&mut target.enum_values, source.enum_values);
-
- fn overwrite<T>(target: &mut Option<T>, source: Option<T>) {
- if let Some(source) = source {
- *target = Some(source);
- }
- }
+ for parameterized_json_schema in inventory::iter::<ParameterizedJsonSchema>() {
+ (parameterized_json_schema.add_and_get_ref)(&mut generator, schema_params, cx);
}
const ZED_SETTINGS: &str = "ZedSettings";
- let RootSchema {
- meta_schema,
- schema: zed_settings_schema,
- mut definitions,
- } = combined_schema;
- definitions.insert(ZED_SETTINGS.to_string(), zed_settings_schema.into());
- let zed_settings_ref = Schema::new_ref(format!("#/definitions/{ZED_SETTINGS}"));
+ let old_zed_settings_definition = generator
+ .definitions_mut()
+ .insert(ZED_SETTINGS.to_string(), combined_schema);
+ assert_eq!(old_zed_settings_definition, None);
+ let zed_settings_ref = schemars::Schema::new_ref(format!("#/definitions/{ZED_SETTINGS}"));
+
+ let mut root_schema = if let Some(meta_schema) = generator.settings().meta_schema.as_ref() {
+ json_schema!({ "$schema": meta_schema.to_string() })
+ } else {
+ json_schema!({})
+ };
// settings file contents matches ZedSettings + overrides for each release stage
- let mut root_schema = json!({
- "allOf": [
+ root_schema.insert(
+ "allOf".to_string(),
+ json!([
zed_settings_ref,
{
"properties": {
@@ -988,17 +1018,14 @@ impl SettingsStore {
"preview": zed_settings_ref,
}
}
- ],
- "definitions": definitions,
- });
-
- if let Some(meta_schema) = meta_schema {
- if let Some(root_schema_object) = root_schema.as_object_mut() {
- root_schema_object.insert("$schema".to_string(), meta_schema.into());
- }
- }
+ ]),
+ );
+ root_schema.insert(
+ "definitions".to_string(),
+ generator.take_definitions(true).into(),
+ );
- root_schema
+ root_schema.to_value()
}
fn recompute_values(
@@ -1311,13 +1338,8 @@ impl<T: Settings> AnySettingValue for SettingValue<T> {
}
}
- fn json_schema(
- &self,
- generator: &mut SchemaGenerator,
- params: &SettingsJsonSchemaParams,
- cx: &App,
- ) -> RootSchema {
- T::json_schema(generator, params, cx)
+ fn json_schema(&self, generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
+ T::FileContent::json_schema(generator)
}
fn edits_for_update(
@@ -2,7 +2,9 @@ use std::sync::Arc;
use gpui::{App, FontFeatures, FontWeight};
use settings::{EditableSettingControl, Settings};
-use theme::{FontFamilyCache, SystemAppearance, ThemeMode, ThemeRegistry, ThemeSettings};
+use theme::{
+ FontFamilyCache, FontFamilyName, SystemAppearance, ThemeMode, ThemeRegistry, ThemeSettings,
+};
use ui::{
CheckboxWithLabel, ContextMenu, DropdownMenu, NumericStepper, SettingsContainer, SettingsGroup,
ToggleButton, prelude::*,
@@ -189,7 +191,7 @@ impl EditableSettingControl for UiFontFamilyControl {
value: Self::Value,
_cx: &App,
) {
- settings.ui_font_family = Some(value.to_string());
+ settings.ui_font_family = Some(FontFamilyName(value.into()));
}
}
@@ -1,11 +1,8 @@
use collections::HashMap;
-use schemars::{
- JsonSchema,
- r#gen::SchemaSettings,
- schema::{ObjectValidation, Schema, SchemaObject},
-};
+use schemars::{JsonSchema, json_schema};
use serde::Deserialize;
use serde_json_lenient::Value;
+use std::borrow::Cow;
#[derive(Deserialize)]
pub struct VsSnippetsFile {
@@ -15,29 +12,25 @@ pub struct VsSnippetsFile {
impl VsSnippetsFile {
pub fn generate_json_schema() -> Value {
- let schema = SchemaSettings::draft07()
- .with(|settings| settings.option_add_null_type = false)
+ let schema = schemars::generate::SchemaSettings::draft07()
.into_generator()
- .into_root_schema_for::<Self>();
+ .root_schema_for::<Self>();
serde_json_lenient::to_value(schema).unwrap()
}
}
impl JsonSchema for VsSnippetsFile {
- fn schema_name() -> String {
+ fn schema_name() -> Cow<'static, str> {
"VsSnippetsFile".into()
}
- fn json_schema(r#gen: &mut schemars::r#gen::SchemaGenerator) -> Schema {
- SchemaObject {
- object: Some(Box::new(ObjectValidation {
- additional_properties: Some(Box::new(r#gen.subschema_for::<VsCodeSnippet>())),
- ..Default::default()
- })),
- ..Default::default()
- }
- .into()
+ fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
+ let snippet_schema = generator.subschema_for::<VsCodeSnippet>();
+ json_schema!({
+ "type": "object",
+ "additionalProperties": snippet_schema
+ })
}
}
@@ -287,7 +287,8 @@ pub struct DebugTaskFile(pub Vec<DebugScenario>);
impl DebugTaskFile {
pub fn generate_json_schema(schemas: &AdapterSchemas) -> serde_json_lenient::Value {
- let build_task_schema = schemars::schema_for!(BuildTaskDefinition);
+ let mut generator = schemars::generate::SchemaSettings::draft07().into_generator();
+ let build_task_schema = generator.root_schema_for::<BuildTaskDefinition>();
let mut build_task_value =
serde_json_lenient::to_value(&build_task_schema).unwrap_or_default();
@@ -1,33 +1,6 @@
-use schemars::{
- SchemaGenerator,
- schema::{ArrayValidation, InstanceType, Schema, SchemaObject, SingleOrVec, StringValidation},
-};
use serde::de::{self, Deserializer, Visitor};
use std::fmt;
-/// Generates a JSON schema for a non-empty string array.
-pub fn non_empty_string_vec_json_schema(_: &mut SchemaGenerator) -> Schema {
- Schema::Object(SchemaObject {
- instance_type: Some(InstanceType::Array.into()),
- array: Some(Box::new(ArrayValidation {
- unique_items: Some(true),
- items: Some(SingleOrVec::Single(Box::new(Schema::Object(
- SchemaObject {
- instance_type: Some(InstanceType::String.into()),
- string: Some(Box::new(StringValidation {
- min_length: Some(1), // Ensures string in the array is non-empty
- ..Default::default()
- })),
- ..Default::default()
- },
- )))),
- ..Default::default()
- })),
- format: Some("vec-of-non-empty-strings".to_string()), // Use a custom format keyword
- ..Default::default()
- })
-}
-
/// Deserializes a non-empty string array.
pub fn non_empty_string_vec<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
@@ -1,6 +1,6 @@
use anyhow::{Context as _, bail};
use collections::{HashMap, HashSet};
-use schemars::{JsonSchema, r#gen::SchemaSettings};
+use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::path::PathBuf;
@@ -9,8 +9,7 @@ use util::{ResultExt, truncate_and_remove_front};
use crate::{
AttachRequest, ResolvedTask, RevealTarget, Shell, SpawnInTerminal, TaskContext, TaskId,
- VariableName, ZED_VARIABLE_NAME_PREFIX,
- serde_helpers::{non_empty_string_vec, non_empty_string_vec_json_schema},
+ VariableName, ZED_VARIABLE_NAME_PREFIX, serde_helpers::non_empty_string_vec,
};
/// A template definition of a Zed task to run.
@@ -61,7 +60,7 @@ pub struct TaskTemplate {
/// Represents the tags which this template attaches to.
/// Adding this removes this task from other UI and gives you ability to run it by tag.
#[serde(default, deserialize_with = "non_empty_string_vec")]
- #[schemars(schema_with = "non_empty_string_vec_json_schema")]
+ #[schemars(length(min = 1))]
pub tags: Vec<String>,
/// Which shell to use when spawning the task.
#[serde(default)]
@@ -116,10 +115,9 @@ pub struct TaskTemplates(pub Vec<TaskTemplate>);
impl TaskTemplates {
/// Generates JSON schema of Tasks JSON template format.
pub fn generate_json_schema() -> serde_json_lenient::Value {
- let schema = SchemaSettings::draft07()
- .with(|settings| settings.option_add_null_type = false)
+ let schema = schemars::generate::SchemaSettings::draft07()
.into_generator()
- .into_root_schema_for::<Self>();
+ .root_schema_for::<Self>();
serde_json_lenient::to_value(schema).unwrap()
}
@@ -2,14 +2,14 @@ use alacritty_terminal::vte::ansi::{
CursorShape as AlacCursorShape, CursorStyle as AlacCursorStyle,
};
use collections::HashMap;
-use gpui::{
- AbsoluteLength, App, FontFallbacks, FontFeatures, FontWeight, Pixels, SharedString, px,
-};
-use schemars::{JsonSchema, r#gen::SchemaGenerator, schema::RootSchema};
+use gpui::{AbsoluteLength, App, FontFallbacks, FontFeatures, FontWeight, Pixels, px};
+use schemars::JsonSchema;
use serde_derive::{Deserialize, Serialize};
-use settings::{SettingsJsonSchemaParams, SettingsSources, add_references_to_properties};
+
+use settings::SettingsSources;
use std::path::PathBuf;
use task::Shell;
+use theme::FontFamilyName;
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
@@ -29,7 +29,7 @@ pub struct TerminalSettings {
pub shell: Shell,
pub working_directory: WorkingDirectory,
pub font_size: Option<Pixels>,
- pub font_family: Option<SharedString>,
+ pub font_family: Option<FontFamilyName>,
pub font_fallbacks: Option<FontFallbacks>,
pub font_features: Option<FontFeatures>,
pub font_weight: Option<FontWeight>,
@@ -147,13 +147,14 @@ pub struct TerminalSettingsContent {
///
/// If this option is not included,
/// the terminal will default to matching the buffer's font family.
- pub font_family: Option<String>,
+ pub font_family: Option<FontFamilyName>,
/// Sets the terminal's font fallbacks.
///
/// If this option is not included,
/// the terminal will default to matching the buffer's font fallbacks.
- pub font_fallbacks: Option<Vec<String>>,
+ #[schemars(extend("uniqueItems" = true))]
+ pub font_fallbacks: Option<Vec<FontFamilyName>>,
/// Sets the terminal's line height.
///
@@ -234,33 +235,13 @@ impl settings::Settings for TerminalSettings {
sources.json_merge()
}
- fn json_schema(
- generator: &mut SchemaGenerator,
- params: &SettingsJsonSchemaParams,
- _: &App,
- ) -> RootSchema {
- let mut root_schema = generator.root_schema_for::<Self::FileContent>();
- root_schema.definitions.extend([
- ("FontFamilies".into(), params.font_family_schema()),
- ("FontFallbacks".into(), params.font_fallback_schema()),
- ]);
-
- add_references_to_properties(
- &mut root_schema,
- &[
- ("font_family", "#/definitions/FontFamilies"),
- ("font_fallbacks", "#/definitions/FontFallbacks"),
- ],
- );
-
- root_schema
- }
-
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
let name = |s| format!("terminal.integrated.{s}");
vscode.f32_setting(&name("fontSize"), &mut current.font_size);
- vscode.string_setting(&name("fontFamily"), &mut current.font_family);
+ if let Some(font_family) = vscode.read_string(&name("fontFamily")) {
+ current.font_family = Some(FontFamilyName(font_family.into()));
+ }
vscode.bool_setting(&name("copyOnSelection"), &mut current.copy_on_select);
vscode.bool_setting("macOptionIsMeta", &mut current.option_as_meta);
vscode.usize_setting("scrollback", &mut current.max_scroll_history_lines);
@@ -682,11 +682,10 @@ impl Element for TerminalElement {
let terminal_settings = TerminalSettings::get_global(cx);
- let font_family = terminal_settings
- .font_family
- .as_ref()
- .unwrap_or(&settings.buffer_font.family)
- .clone();
+ let font_family = terminal_settings.font_family.as_ref().map_or_else(
+ || settings.buffer_font.family.clone(),
+ |font_family| font_family.0.clone().into(),
+ );
let font_fallbacks = terminal_settings
.font_fallbacks
@@ -24,6 +24,7 @@ fs.workspace = true
futures.workspace = true
gpui.workspace = true
indexmap.workspace = true
+inventory.workspace = true
log.workspace = true
palette = { workspace = true, default-features = false, features = ["std"] }
parking_lot.workspace = true
@@ -4,12 +4,11 @@ use anyhow::Result;
use gpui::{FontStyle, FontWeight, HighlightStyle, Hsla, WindowBackgroundAppearance};
use indexmap::IndexMap;
use palette::FromColor;
-use schemars::JsonSchema;
-use schemars::r#gen::SchemaGenerator;
-use schemars::schema::{Schema, SchemaObject};
+use schemars::{JsonSchema, json_schema};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use serde_repr::{Deserialize_repr, Serialize_repr};
+use std::borrow::Cow;
use crate::{StatusColorsRefinement, ThemeColorsRefinement};
@@ -1502,30 +1501,15 @@ pub enum FontWeightContent {
}
impl JsonSchema for FontWeightContent {
- fn schema_name() -> String {
- "FontWeightContent".to_owned()
+ fn schema_name() -> Cow<'static, str> {
+ "FontWeightContent".into()
}
- fn is_referenceable() -> bool {
- false
- }
-
- fn json_schema(_: &mut SchemaGenerator) -> Schema {
- SchemaObject {
- enum_values: Some(vec![
- 100.into(),
- 200.into(),
- 300.into(),
- 400.into(),
- 500.into(),
- 600.into(),
- 700.into(),
- 800.into(),
- 900.into(),
- ]),
- ..Default::default()
- }
- .into()
+ fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
+ json_schema!({
+ "type": "integer",
+ "enum": [100, 200, 300, 400, 500, 600, 700, 800, 900]
+ })
}
}
@@ -7,17 +7,12 @@ use anyhow::Result;
use derive_more::{Deref, DerefMut};
use gpui::{
App, Context, Font, FontFallbacks, FontFeatures, FontStyle, FontWeight, Global, Pixels,
- Subscription, Window, px,
+ SharedString, Subscription, Window, px,
};
use refineable::Refineable;
-use schemars::{
- JsonSchema,
- r#gen::SchemaGenerator,
- schema::{InstanceType, Schema, SchemaObject},
-};
+use schemars::{JsonSchema, json_schema};
use serde::{Deserialize, Serialize};
-use serde_json::Value;
-use settings::{Settings, SettingsJsonSchemaParams, SettingsSources, add_references_to_properties};
+use settings::{ParameterizedJsonSchema, Settings, SettingsSources, replace_subschema};
use std::sync::Arc;
use util::ResultExt as _;
@@ -263,25 +258,19 @@ impl Global for AgentFontSize {}
#[serde(untagged)]
pub enum ThemeSelection {
/// A static theme selection, represented by a single theme name.
- Static(#[schemars(schema_with = "theme_name_ref")] String),
+ 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.
- #[schemars(schema_with = "theme_name_ref")]
- light: String,
+ light: ThemeName,
/// The theme to use for dark mode.
- #[schemars(schema_with = "theme_name_ref")]
- dark: String,
+ dark: ThemeName,
},
}
-fn theme_name_ref(_: &mut SchemaGenerator) -> Schema {
- Schema::new_ref("#/definitions/ThemeName".into())
-}
-
// TODO: Rename ThemeMode -> ThemeAppearanceMode
/// The mode use to select a theme.
///
@@ -306,13 +295,13 @@ impl ThemeSelection {
/// Returns the theme name for the selected [ThemeMode].
pub fn theme(&self, system_appearance: Appearance) -> &str {
match self {
- Self::Static(theme) => theme,
+ Self::Static(theme) => &theme.0,
Self::Dynamic { mode, light, dark } => match mode {
- ThemeMode::Light => light,
- ThemeMode::Dark => dark,
+ ThemeMode::Light => &light.0,
+ ThemeMode::Dark => &dark.0,
ThemeMode::System => match system_appearance {
- Appearance::Light => light,
- Appearance::Dark => dark,
+ Appearance::Light => &light.0,
+ Appearance::Dark => &dark.0,
},
},
}
@@ -327,27 +316,21 @@ impl ThemeSelection {
}
}
-fn icon_theme_name_ref(_: &mut SchemaGenerator) -> Schema {
- Schema::new_ref("#/definitions/IconThemeName".into())
-}
-
/// 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(#[schemars(schema_with = "icon_theme_name_ref")] String),
+ 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.
- #[schemars(schema_with = "icon_theme_name_ref")]
- light: String,
+ light: IconThemeName,
/// The icon theme to use for dark mode.
- #[schemars(schema_with = "icon_theme_name_ref")]
- dark: String,
+ dark: IconThemeName,
},
}
@@ -355,13 +338,13 @@ impl IconThemeSelection {
/// Returns the icon theme name based on the given [`Appearance`].
pub fn icon_theme(&self, system_appearance: Appearance) -> &str {
match self {
- Self::Static(theme) => theme,
+ Self::Static(theme) => &theme.0,
Self::Dynamic { mode, light, dark } => match mode {
- ThemeMode::Light => light,
- ThemeMode::Dark => dark,
+ ThemeMode::Light => &light.0,
+ ThemeMode::Dark => &dark.0,
ThemeMode::System => match system_appearance {
- Appearance::Light => light,
- Appearance::Dark => dark,
+ Appearance::Light => &light.0,
+ Appearance::Dark => &dark.0,
},
},
}
@@ -384,11 +367,12 @@ pub struct ThemeSettingsContent {
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<String>,
+ pub ui_font_family: Option<FontFamilyName>,
/// The font fallbacks to use for rendering in the UI.
#[serde(default)]
#[schemars(default = "default_font_fallbacks")]
- pub ui_font_fallbacks: Option<Vec<String>>,
+ #[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")]
@@ -398,11 +382,11 @@ pub struct ThemeSettingsContent {
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<String>,
+ pub buffer_font_family: Option<FontFamilyName>,
/// The font fallbacks to use for rendering in text buffers.
#[serde(default)]
- #[schemars(default = "default_font_fallbacks")]
- pub buffer_font_fallbacks: Option<Vec<String>>,
+ #[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>,
@@ -467,9 +451,9 @@ impl ThemeSettingsContent {
},
};
- *theme_to_update = theme_name.to_string();
+ *theme_to_update = ThemeName(theme_name.into());
} else {
- self.theme = Some(ThemeSelection::Static(theme_name.to_string()));
+ self.theme = Some(ThemeSelection::Static(ThemeName(theme_name.into())));
}
}
@@ -488,9 +472,11 @@ impl ThemeSettingsContent {
},
};
- *icon_theme_to_update = icon_theme_name.to_string();
+ *icon_theme_to_update = IconThemeName(icon_theme_name.into());
} else {
- self.icon_theme = Some(IconThemeSelection::Static(icon_theme_name.to_string()));
+ self.icon_theme = Some(IconThemeSelection::Static(IconThemeName(
+ icon_theme_name.into(),
+ )));
}
}
@@ -516,8 +502,8 @@ impl ThemeSettingsContent {
} else {
self.theme = Some(ThemeSelection::Dynamic {
mode,
- light: ThemeSettings::DEFAULT_LIGHT_THEME.into(),
- dark: ThemeSettings::DEFAULT_DARK_THEME.into(),
+ light: ThemeName(ThemeSettings::DEFAULT_LIGHT_THEME.into()),
+ dark: ThemeName(ThemeSettings::DEFAULT_DARK_THEME.into()),
});
}
@@ -539,7 +525,9 @@ impl ThemeSettingsContent {
} => *mode_to_update = mode,
}
} else {
- self.icon_theme = Some(IconThemeSelection::Static(DEFAULT_ICON_THEME_NAME.into()));
+ self.icon_theme = Some(IconThemeSelection::Static(IconThemeName(
+ DEFAULT_ICON_THEME_NAME.into(),
+ )));
}
}
}
@@ -815,26 +803,39 @@ impl settings::Settings for ThemeSettings {
let themes = ThemeRegistry::default_global(cx);
let system_appearance = SystemAppearance::default_global(cx);
+ fn font_fallbacks_from_settings(
+ fallbacks: Option<Vec<FontFamilyName>>,
+ ) -> Option<FontFallbacks> {
+ fallbacks.map(|fallbacks| {
+ FontFallbacks::from_fonts(
+ fallbacks
+ .into_iter()
+ .map(|font_family| font_family.0.to_string())
+ .collect(),
+ )
+ })
+ }
+
let defaults = sources.default;
let mut this = Self {
ui_font_size: defaults.ui_font_size.unwrap().into(),
ui_font: Font {
- family: defaults.ui_font_family.as_ref().unwrap().clone().into(),
+ family: defaults.ui_font_family.as_ref().unwrap().0.clone().into(),
features: defaults.ui_font_features.clone().unwrap(),
- fallbacks: defaults
- .ui_font_fallbacks
- .as_ref()
- .map(|fallbacks| FontFallbacks::from_fonts(fallbacks.clone())),
+ fallbacks: font_fallbacks_from_settings(defaults.ui_font_fallbacks.clone()),
weight: defaults.ui_font_weight.map(FontWeight).unwrap(),
style: Default::default(),
},
buffer_font: Font {
- family: defaults.buffer_font_family.as_ref().unwrap().clone().into(),
- features: defaults.buffer_font_features.clone().unwrap(),
- fallbacks: defaults
- .buffer_font_fallbacks
+ family: defaults
+ .buffer_font_family
.as_ref()
- .map(|fallbacks| FontFallbacks::from_fonts(fallbacks.clone())),
+ .unwrap()
+ .0
+ .clone()
+ .into(),
+ features: defaults.buffer_font_features.clone().unwrap(),
+ fallbacks: font_fallbacks_from_settings(defaults.buffer_font_fallbacks.clone()),
weight: defaults.buffer_font_weight.map(FontWeight).unwrap(),
style: FontStyle::default(),
},
@@ -872,26 +873,26 @@ impl settings::Settings for ThemeSettings {
}
if let Some(value) = value.buffer_font_family.clone() {
- this.buffer_font.family = value.into();
+ this.buffer_font.family = value.0.into();
}
if let Some(value) = value.buffer_font_features.clone() {
this.buffer_font.features = value;
}
if let Some(value) = value.buffer_font_fallbacks.clone() {
- this.buffer_font.fallbacks = Some(FontFallbacks::from_fonts(value));
+ this.buffer_font.fallbacks = font_fallbacks_from_settings(Some(value));
}
if let Some(value) = value.buffer_font_weight {
this.buffer_font.weight = clamp_font_weight(value);
}
if let Some(value) = value.ui_font_family.clone() {
- this.ui_font.family = value.into();
+ this.ui_font.family = value.0.into();
}
if let Some(value) = value.ui_font_features.clone() {
this.ui_font.features = value;
}
if let Some(value) = value.ui_font_fallbacks.clone() {
- this.ui_font.fallbacks = Some(FontFallbacks::from_fonts(value));
+ this.ui_font.fallbacks = font_fallbacks_from_settings(Some(value));
}
if let Some(value) = value.ui_font_weight {
this.ui_font.weight = clamp_font_weight(value);
@@ -959,64 +960,73 @@ impl settings::Settings for ThemeSettings {
Ok(this)
}
- fn json_schema(
- generator: &mut SchemaGenerator,
- params: &SettingsJsonSchemaParams,
- cx: &App,
- ) -> schemars::schema::RootSchema {
- let mut root_schema = generator.root_schema_for::<ThemeSettingsContent>();
- let theme_names = ThemeRegistry::global(cx)
- .list_names()
- .into_iter()
- .map(|theme_name| Value::String(theme_name.to_string()))
- .collect();
-
- let theme_name_schema = SchemaObject {
- instance_type: Some(InstanceType::String.into()),
- enum_values: Some(theme_names),
- ..Default::default()
- };
-
- let icon_theme_names = ThemeRegistry::global(cx)
- .list_icon_themes()
- .into_iter()
- .map(|icon_theme| Value::String(icon_theme.name.to_string()))
- .collect();
-
- let icon_theme_name_schema = SchemaObject {
- instance_type: Some(InstanceType::String.into()),
- enum_values: Some(icon_theme_names),
- ..Default::default()
- };
-
- root_schema.definitions.extend([
- ("ThemeName".into(), theme_name_schema.into()),
- ("IconThemeName".into(), icon_theme_name_schema.into()),
- ("FontFamilies".into(), params.font_family_schema()),
- ("FontFallbacks".into(), params.font_fallback_schema()),
- ]);
-
- add_references_to_properties(
- &mut root_schema,
- &[
- ("buffer_font_family", "#/definitions/FontFamilies"),
- ("buffer_font_fallbacks", "#/definitions/FontFallbacks"),
- ("ui_font_family", "#/definitions/FontFamilies"),
- ("ui_font_fallbacks", "#/definitions/FontFallbacks"),
- ],
- );
-
- root_schema
- }
-
fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
vscode.f32_setting("editor.fontWeight", &mut current.buffer_font_weight);
vscode.f32_setting("editor.fontSize", &mut current.buffer_font_size);
- vscode.string_setting("editor.font", &mut current.buffer_font_family);
+ if let Some(font) = vscode.read_string("editor.font") {
+ current.buffer_font_family = Some(FontFamilyName(font.into()));
+ }
// TODO: possibly map editor.fontLigatures to buffer_font_features?
}
}
+/// 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| {
+ let schema = json_schema!({
+ "type": "string",
+ "enum": ThemeRegistry::global(cx).list_names(),
+ });
+ replace_subschema::<ThemeName>(generator, schema)
+ }
+ }
+}
+
+/// 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| {
+ let schema = json_schema!({
+ "type": "string",
+ "enum": ThemeRegistry::global(cx)
+ .list_icon_themes()
+ .into_iter()
+ .map(|icon_theme| icon_theme.name)
+ .collect::<Vec<SharedString>>(),
+ });
+ replace_subschema::<IconThemeName>(generator, schema)
+ }
+ }
+}
+
+/// 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| {
+ let schema = json_schema!({
+ "type": "string",
+ "enum": params.font_names,
+ });
+ replace_subschema::<FontFamilyName>(generator, schema)
+ }
+ }
+}
+
fn merge<T: Copy>(target: &mut T, value: Option<T>) {
if let Some(value) = value {
*target = value;
@@ -711,7 +711,7 @@ mod test {
);
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
- settings.languages.insert(
+ settings.languages.0.insert(
LanguageName::new("Rust"),
LanguageSettingsContent {
auto_indent_on_paste: Some(false),
@@ -111,7 +111,7 @@ sea-orm = { version = "1", features = ["runtime-tokio-rustls", "sqlx-postgres",
sea-query-binder = { version = "0.7", default-features = false, features = ["postgres-array", "sqlx-postgres", "sqlx-sqlite", "with-bigdecimal", "with-chrono", "with-json", "with-rust_decimal", "with-time", "with-uuid"] }
semver = { version = "1", features = ["serde"] }
serde = { version = "1", features = ["alloc", "derive", "rc"] }
-serde_json = { version = "1", features = ["preserve_order", "raw_value", "unbounded_depth"] }
+serde_json = { version = "1", features = ["alloc", "preserve_order", "raw_value", "unbounded_depth"] }
sha1 = { version = "0.10", features = ["compress"] }
simd-adler32 = { version = "0.3" }
smallvec = { version = "1", default-features = false, features = ["const_new", "serde", "union", "write"] }
@@ -244,7 +244,7 @@ sea-query-binder = { version = "0.7", default-features = false, features = ["pos
semver = { version = "1", features = ["serde"] }
serde = { version = "1", features = ["alloc", "derive", "rc"] }
serde_derive = { version = "1", features = ["deserialize_in_place"] }
-serde_json = { version = "1", features = ["preserve_order", "raw_value", "unbounded_depth"] }
+serde_json = { version = "1", features = ["alloc", "preserve_order", "raw_value", "unbounded_depth"] }
sha1 = { version = "0.10", features = ["compress"] }
simd-adler32 = { version = "0.3" }
smallvec = { version = "1", default-features = false, features = ["const_new", "serde", "union", "write"] }