Default `#[schemars(deny_unknown_fields)] for json-language-server schemas (#33883)
Followup to #33678, doing the same thing for all JSON Schema files provided to json-language-server Release Notes: * Added warnings for unknown fields when editing `tasks.json` / `snippets.json`.
This commit is contained in:
parent
38544e514a
commit
ed7552d3e3
14 changed files with 136 additions and 149 deletions
|
@ -427,6 +427,10 @@ impl KeymapFile {
|
|||
}
|
||||
|
||||
pub fn generate_json_schema_for_registered_actions(cx: &mut App) -> Value {
|
||||
// instead of using DefaultDenyUnknownFields, actions typically use
|
||||
// `#[serde(deny_unknown_fields)]` so that these cases are reported as parse failures. This
|
||||
// is because the rest of the keymap will still load in these cases, whereas other settings
|
||||
// files would not.
|
||||
let mut generator = schemars::generate::SchemaSettings::draft2019_09().into_generator();
|
||||
|
||||
let action_schemas = cx.action_schemas(&mut generator);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use anyhow::Result;
|
||||
use gpui::App;
|
||||
use schemars::{JsonSchema, Schema, transform::transform_subschemas};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use serde_json::Value;
|
||||
use std::{ops::Range, sync::LazyLock};
|
||||
|
@ -21,63 +20,6 @@ pub struct ParameterizedJsonSchema {
|
|||
|
||||
inventory::collect!(ParameterizedJsonSchema);
|
||||
|
||||
const DEFS_PATH: &str = "#/$defs/";
|
||||
|
||||
/// Replaces the JSON schema definition for some type if it is in use (in the definitions list), and
|
||||
/// returns a reference to it.
|
||||
///
|
||||
/// This asserts that JsonSchema::schema_name() + "2" does not exist because this indicates that
|
||||
/// there are multiple types that use this name, and unfortunately schemars APIs do not support
|
||||
/// resolving this ambiguity - see https://github.com/GREsau/schemars/issues/449
|
||||
///
|
||||
/// This takes a closure for `schema` because some settings types are not available on the remote
|
||||
/// server, and so will crash when attempting to access e.g. GlobalThemeRegistry.
|
||||
pub fn replace_subschema<T: JsonSchema>(
|
||||
generator: &mut schemars::SchemaGenerator,
|
||||
schema: impl Fn() -> schemars::Schema,
|
||||
) -> schemars::Schema {
|
||||
// fallback on just using the schema name, which could collide.
|
||||
let schema_name = T::schema_name();
|
||||
let definitions = generator.definitions_mut();
|
||||
assert!(!definitions.contains_key(&format!("{schema_name}2")));
|
||||
if definitions.contains_key(schema_name.as_ref()) {
|
||||
definitions.insert(schema_name.to_string(), schema().to_value());
|
||||
}
|
||||
Schema::new_ref(format!("{DEFS_PATH}{schema_name}"))
|
||||
}
|
||||
|
||||
/// Adds a new JSON schema definition and returns a reference to it. **Panics** if the name is
|
||||
/// already in use.
|
||||
pub fn add_new_subschema(
|
||||
generator: &mut schemars::SchemaGenerator,
|
||||
name: &str,
|
||||
schema: Value,
|
||||
) -> Schema {
|
||||
let old_definition = generator.definitions_mut().insert(name.to_string(), schema);
|
||||
assert_eq!(old_definition, None);
|
||||
schemars::Schema::new_ref(format!("{DEFS_PATH}{name}"))
|
||||
}
|
||||
|
||||
/// Defaults `additionalProperties` to `true`, as if `#[schemars(deny_unknown_fields)]` was on every
|
||||
/// struct. Skips structs that have `additionalProperties` set (such as if #[serde(flatten)] is used
|
||||
/// on a map).
|
||||
#[derive(Clone)]
|
||||
pub struct DefaultDenyUnknownFields;
|
||||
|
||||
impl schemars::transform::Transform for DefaultDenyUnknownFields {
|
||||
fn transform(&mut self, schema: &mut schemars::Schema) {
|
||||
if let Some(object) = schema.as_object_mut() {
|
||||
if object.contains_key("properties")
|
||||
&& !object.contains_key("additionalProperties")
|
||||
&& !object.contains_key("unevaluatedProperties")
|
||||
{
|
||||
object.insert("additionalProperties".to_string(), false.into());
|
||||
}
|
||||
}
|
||||
transform_subschemas(self, schema);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_value_in_json_text<'a>(
|
||||
text: &mut String,
|
||||
key_path: &mut Vec<&'a str>,
|
||||
|
|
|
@ -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, json_schema};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||
use serde_json::{Value, json};
|
||||
use smallvec::SmallVec;
|
||||
|
@ -18,14 +18,16 @@ use std::{
|
|||
str::{self, FromStr},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use util::{ResultExt as _, merge_non_null_json_value_into};
|
||||
use util::{
|
||||
ResultExt as _, merge_non_null_json_value_into,
|
||||
schemars::{DefaultDenyUnknownFields, add_new_subschema},
|
||||
};
|
||||
|
||||
pub type EditorconfigProperties = ec4rs::Properties;
|
||||
|
||||
use crate::{
|
||||
DefaultDenyUnknownFields, ParameterizedJsonSchema, SettingsJsonSchemaParams, VsCodeSettings,
|
||||
WorktreeId, add_new_subschema, 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.
|
||||
|
@ -1019,19 +1021,19 @@ impl SettingsStore {
|
|||
.unwrap()
|
||||
.remove("additionalProperties");
|
||||
|
||||
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!({})
|
||||
};
|
||||
let meta_schema = generator
|
||||
.settings()
|
||||
.meta_schema
|
||||
.as_ref()
|
||||
.expect("meta_schema should be present in schemars settings")
|
||||
.to_string();
|
||||
|
||||
// "unevaluatedProperties: false" to report unknown fields.
|
||||
root_schema.insert("unevaluatedProperties".to_string(), false.into());
|
||||
|
||||
// Settings file contents matches ZedSettings + overrides for each release stage.
|
||||
root_schema.insert(
|
||||
"allOf".to_string(),
|
||||
json!([
|
||||
json!({
|
||||
"$schema": meta_schema,
|
||||
"title": "Zed Settings",
|
||||
"unevaluatedProperties": false,
|
||||
// ZedSettings + settings overrides for each release stage
|
||||
"allOf": [
|
||||
zed_settings_ref,
|
||||
{
|
||||
"properties": {
|
||||
|
@ -1041,12 +1043,9 @@ impl SettingsStore {
|
|||
"preview": zed_release_stage_settings_ref,
|
||||
}
|
||||
}
|
||||
]),
|
||||
);
|
||||
|
||||
root_schema.insert("$defs".to_string(), definitions.into());
|
||||
|
||||
root_schema.to_value()
|
||||
],
|
||||
"$defs": definitions,
|
||||
})
|
||||
}
|
||||
|
||||
fn recompute_values(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue