Add knot.toml schema (#15735)

## Summary

Adds a JSON schema generation step for Red Knot. This PR doesn't yet add
a publishing step because it's still a bit early for that


## Test plan

I tested the schema in Zed, VS Code and PyCharm:

* PyCharm: You have to manually add a schema mapping (settings JSON
Schema Mappings)
* Zed and VS code support the inline schema specification

```toml
#:schema /Users/micha/astral/ruff/knot.schema.json


[environment]
extra-paths = []


[rules]
call-possibly-unbound-method = "error"
unknown-rule = "error"

# duplicate-base = "error"
```

```json
{
    "$schema": "file:///Users/micha/astral/ruff/knot.schema.json",

    "environment": {
        "python-version": "3.13",
        "python-platform": "linux2"
    },

    "rules": {
        "unknown-rule": "error"
    }
}
```


https://github.com/user-attachments/assets/a18fcd96-7cbe-4110-985b-9f1935584411


The Schema overall works but all editors have their own quirks:

* PyCharm: Hovering a name always shows the section description instead
of the description of the specific setting. But it's the same for other
settings in `pyproject.toml` files 🤷
* VS Code (JSON): Using the generated schema in a JSON file gives
exactly the experience I want
* VS Code (TOML): 
* Properties with multiple possible values are repeated during
auto-completion without giving any hint how they're different. ![Screen
Shot 2025-02-06 at 14 05 35
PM](https://github.com/user-attachments/assets/d7f3c2a9-2351-4226-9fc1-b91aa192a237)
* The property description mushes together the description of the
property and the value, which looks sort of ridiculous. ![Screen Shot
2025-02-06 at 14 04 40
PM](https://github.com/user-attachments/assets/8b72f04a-c62a-49b5-810f-7ddd472884d0)
* Autocompletion and documentation hovering works (except the
limitations mentioned above)
* Zed:
* Very similar to VS Code with the exception that it uses the
description attribute to distinguish settings with multiple possible
values ![Screen Shot 2025-02-06 at 14 08 19
PM](https://github.com/user-attachments/assets/78a7f849-ff4e-44ff-8317-708eaf02dc1f)


I don't think there's much we can do here other than hope (or help)
editors improve their auto completion. The same short comings also apply
to ruff, so this isn't something new. For now, I think this is good
enough
This commit is contained in:
Micha Reiser 2025-02-07 09:59:40 +00:00 committed by GitHub
parent 7db5a924af
commit 26c37b1e0e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 1087 additions and 96 deletions

View file

@ -11,6 +11,7 @@ pub enum PythonPlatform {
/// Do not make any assumptions about the target platform.
#[default]
All,
/// Assume a specific target platform like `linux`, `darwin` or `win32`.
///
/// We use a string (instead of individual enum variants), as the set of possible platforms
@ -28,3 +29,77 @@ impl Display for PythonPlatform {
}
}
}
#[cfg(feature = "schemars")]
mod schema {
use crate::PythonPlatform;
use schemars::_serde_json::Value;
use schemars::gen::SchemaGenerator;
use schemars::schema::{Metadata, Schema, SchemaObject, SubschemaValidation};
use schemars::JsonSchema;
impl JsonSchema for PythonPlatform {
fn schema_name() -> String {
"PythonPlatform".to_string()
}
fn json_schema(_gen: &mut SchemaGenerator) -> Schema {
Schema::Object(SchemaObject {
// Hard code some well known values, but allow any other string as well.
subschemas: Some(Box::new(SubschemaValidation {
any_of: Some(vec![
Schema::Object(SchemaObject {
instance_type: Some(schemars::schema::InstanceType::String.into()),
..SchemaObject::default()
}),
// Promote well-known values for better auto-completion.
// Using `const` over `enumValues` as recommended [here](https://github.com/SchemaStore/schemastore/blob/master/CONTRIBUTING.md#documenting-enums).
Schema::Object(SchemaObject {
const_value: Some(Value::String("all".to_string())),
metadata: Some(Box::new(Metadata {
description: Some(
"Do not make any assumptions about the target platform."
.to_string(),
),
..Metadata::default()
})),
..SchemaObject::default()
}),
Schema::Object(SchemaObject {
const_value: Some(Value::String("darwin".to_string())),
metadata: Some(Box::new(Metadata {
description: Some("Darwin".to_string()),
..Metadata::default()
})),
..SchemaObject::default()
}),
Schema::Object(SchemaObject {
const_value: Some(Value::String("linux".to_string())),
metadata: Some(Box::new(Metadata {
description: Some("Linux".to_string()),
..Metadata::default()
})),
..SchemaObject::default()
}),
Schema::Object(SchemaObject {
const_value: Some(Value::String("win32".to_string())),
metadata: Some(Box::new(Metadata {
description: Some("Windows".to_string()),
..Metadata::default()
})),
..SchemaObject::default()
}),
]),
..SubschemaValidation::default()
})),
..SchemaObject::default()
})
}
}
}