Compare commits

...

10 Commits

Author SHA1 Message Date
Piotr Osiewicz
52afd4720c Attempt to list path to the offending setting 2024-08-22 19:13:54 +02:00
Piotr Osiewicz
773c1f16e3 💄 2024-08-22 15:43:18 +02:00
Piotr Osiewicz
42ab97f6a8 Start on schema validator 2024-08-21 18:43:21 +02:00
Piotr Osiewicz
2d74297317 Merge branch 'main' into configuration-docs-generation 2024-08-21 00:51:33 +02:00
Nate Butler
ff027f1570 Add WIP settings-table 2024-08-08 21:00:24 -04:00
Nate Butler
aeff2c633c Merge branch 'md-book-settings-table-ui' into configuration-docs-generation 2024-08-08 20:12:29 -04:00
Danilo Leal
788a6ae14a tweak the status display 2024-08-08 16:25:54 -03:00
Danilo Leal
8cef8da0e7 add tweaks to table design 2024-08-08 16:01:46 -03:00
Piotr Osiewicz
6602f7b2b6 WIP: generate settings schema via cmdline 2024-08-08 20:03:34 +02:00
Nate Butler
39d08e1577 init 2024-08-08 13:23:34 -04:00
11 changed files with 1724 additions and 8 deletions

2
Cargo.lock generated
View File

@@ -13735,6 +13735,8 @@ dependencies = [
"cargo_metadata",
"cargo_toml",
"clap",
"schemars",
"serde_json",
]
[[package]]

View File

@@ -8,7 +8,7 @@ mod zed;
use anyhow::{anyhow, Context as _, Result};
use assistant::PromptBuilder;
use clap::{command, Parser};
use clap::{command, Parser, ValueEnum};
use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
use client::{parse_zed_link, Client, DevServerToken, UserStore};
use collab_ui::channel_view::ChannelView;
@@ -19,7 +19,7 @@ use fs::{Fs, RealFs};
use futures::{future, StreamExt};
use git::GitHostingProviderRegistry;
use gpui::{
Action, App, AppContext, AsyncAppContext, Context, DismissEvent, Global, Task,
Action, App, AppContext, AsyncAppContext, Context, DismissEvent, Global, ReadGlobal as _, Task,
UpdateGlobal as _, VisualContext,
};
use image_viewer;
@@ -536,19 +536,45 @@ fn main() {
}
}
let app_state = app_state.clone();
let state = app_state.clone();
let prompt_builder = prompt_builder.clone();
cx.spawn(move |cx| async move {
while let Some(urls) = open_rx.next().await {
cx.update(|cx| {
if let Some(request) = OpenRequest::parse(urls, cx).log_err() {
handle_open_request(request, app_state.clone(), prompt_builder.clone(), cx);
handle_open_request(request, state.clone(), prompt_builder.clone(), cx);
}
})
.ok();
}
})
.detach();
if let Some(schema) = args.schema {
let app_state = app_state.clone();
cx.spawn(|cx| async move {
match schema {
SchemaKind::Settings => {
let language_names = app_state.languages.language_names();
let schema = cx.update(|cx| {
SettingsStore::global(cx).json_schema(
&settings::SettingsJsonSchemaParams {
staff_mode: false,
language_names: &language_names,
font_names: &[],
},
cx,
)
});
if let Ok(schema) = schema {
if let Ok(schema) = serde_json::to_string(&schema) {
let _ = std::fs::write("settings-schema.json", &(schema));
}
}
}
}
})
.detach();
}
});
}
@@ -1039,6 +1065,11 @@ fn stdout_is_a_pty() -> bool {
std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_none() && std::io::stdout().is_terminal()
}
#[derive(Clone, Debug, PartialEq, ValueEnum)]
enum SchemaKind {
Settings,
}
#[derive(Parser, Debug)]
#[command(name = "zed", disable_version_flag = true)]
struct Args {
@@ -1053,6 +1084,10 @@ struct Args {
/// Instructs zed to run as a dev server on this machine. (not implemented)
#[arg(long)]
dev_server_token: Option<String>,
/// Instructs zed to dump a JSON schema to a file.
#[arg(long)]
schema: Option<SchemaKind>,
}
fn parse_url_arg(arg: &str, cx: &AppContext) -> Result<String> {

View File

@@ -9,8 +9,8 @@ site-url = "/docs/"
[output.html]
no-section-label = true
preferred-dark-theme = "light"
additional-css = ["theme/page-toc.css", "theme/plugins.css"]
additional-js = ["theme/page-toc.js", "theme/plugins.js"]
additional-css = ["theme/page-toc.css", "theme/settings-table.css", "theme/plugins.css"]
additional-js = ["theme/page-toc.js", "theme/settings-table.js", "theme/plugins.js"]
[output.html.print]
enable = false

View File

@@ -29,6 +29,8 @@ Extensions that provide language servers may also provide default settings for t
# Settings
<div id="settings-container"></div>
## Active Pane Magnification
- Description: Scale by which to zoom the active pane. When set to `1.0`, the active pane has the same size as others, but when set to a larger value, the active pane takes up more space.

View File

@@ -44,8 +44,8 @@
--warning-bg: hsl(42, 100%, 60%, 0.1);
--warning-icon: hsl(42, 100%, 30%);
--table-border-color: hsl(219, 93%, 42%, 0.15);
--table-header-bg: hsl(0, 0%, 80%);
--table-border-color: hsl(219, 10%, 42%, 0.15);
--table-header-bg: hsl(219, 18%, 68%, 0.15);
--table-alternate-bg: hsl(0, 0%, 97%);
--searchbar-border-color: #aaa;

95
docs/theme/settings-table.css vendored Normal file
View File

@@ -0,0 +1,95 @@
.settings-list {
display: flex;
flex-direction: column;
font-family: var(--font);
color: var(--fg);
background-color: var(--bg);
}
.setting-container {
padding: 12px 0;
display: flex;
flex-direction: column;
border-bottom: 1px solid var(--table-border-color);
}
.setting-header {
grid-column: span 2 / span 2;
display: flex;
flex-direction: row;
gap: 0.8rem;
padding: 0.5em 1em 0.5rem 0;
}
.setting-line {
display: flex;
flex-direction: row;
gap: 2rem;
/* padding: 0.5em 1em; */
}
.setting-name {
font-family: monospace;
font-size: 1.3rem;
font-weight: bold;
color: var(--sidebar-fg);
}
.setting-type {
font-family: monospace;
font-size: 1.3rem;
color: var(--fg);
}
.setting-description {
font-size: 1.3rem;
color: var(--fg);
padding: 0.5em 0 0.5em 0;
}
.setting-default {
font-family: monospace;
font-size: 1.3rem;
color: var(--fg);
}
.setting-status {
width: fit-content;
padding: 0.2em 0.5em;
background-color: var(--sidebar-active-bg);
font-size: 1.2rem;
font-family: monospace;
color: #000;
}
.setting-details {
grid-column: span 4 / span 4;
width: 100%;
}
.setting-details tr:nth-child(even) {
background-color: transparent;
}
.setting-details td {
padding: 0.5em 0 0.5em 1em;
}
.setting-label {
font-size: 1.2rem;
border: none;
}
.setting-value {
font-size: 1.4rem;
border: none;
}
.settings-list code {
font-family: var(--mono-font);
font-size: 1.4rem;
background-color: var(--quote-bg);
padding: 2px 4px;
border-radius: 3px;
color: var(--inline-code-color);
}

1448
docs/theme/settings-table.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -13,3 +13,5 @@ anyhow.workspace = true
cargo_metadata.workspace = true
cargo_toml.workspace = true
clap = { workspace = true, features = ["derive"] }
schemars.workspace = true
serde_json.workspace = true

View File

@@ -18,6 +18,8 @@ enum CliCommand {
Licenses(tasks::licenses::LicensesArgs),
/// Checks that packages conform to a set of standards.
PackageConformity(tasks::package_conformity::PackageConformityArgs),
/// Checks that settings schema conforms to a set of standards.
ValidateSchema(tasks::validate_schema::SchemaValidationArgs),
}
fn main() -> Result<()> {
@@ -29,5 +31,6 @@ fn main() -> Result<()> {
CliCommand::PackageConformity(args) => {
tasks::package_conformity::run_package_conformity(args)
}
CliCommand::ValidateSchema(args) => tasks::validate_schema::run_validate_schema(args),
}
}

View File

@@ -1,3 +1,4 @@
pub mod clippy;
pub mod licenses;
pub mod package_conformity;
pub mod validate_schema;

View File

@@ -0,0 +1,128 @@
use std::{collections::HashMap, path::PathBuf};
use anyhow::Result;
use clap::Parser;
use schemars::{
schema::{RootSchema, SchemaObject},
visit::Visitor,
};
use serde_json::Value;
#[derive(Parser)]
pub struct SchemaValidationArgs {
path: PathBuf,
}
#[derive(Debug)]
enum Violation {
MissingDefault,
MissingExample,
MissingMetadata,
MissingDescription,
}
struct Violations {
schema: SchemaObject,
violations: Vec<(Vec<String>, Violation)>,
}
#[derive(Default)]
struct SettingsSchemaValidator {
objects: Vec<Violations>,
}
impl SettingsSchemaValidator {
fn validate_schema_object(&mut self, builder: &mut ViolationsBuilder, schema: &SchemaObject) {
if schema.metadata.is_none() {
builder.add(Violation::MissingMetadata);
} else if let Some(metadata) = &schema.metadata {
if metadata.description.is_none() {
builder.add(Violation::MissingDescription);
}
if metadata.default.is_none() {
builder.add(Violation::MissingDefault);
}
if metadata.examples.is_empty() && !Self::is_boolean_schema(schema) {
builder.add(Violation::MissingExample);
}
}
if let Some(object) = &schema.object {
for (property, property_schema) in &object.properties {
builder.push(property.clone());
self.validate_schema_object(builder, &property_schema.clone().into_object());
builder.pop();
}
}
}
fn is_boolean_schema(schema: &SchemaObject) -> bool {
schema.instance_type == Some(schemars::schema::InstanceType::Boolean.into())
}
}
struct ViolationsBuilder<'a> {
violations: &'a mut Violations,
current_path: Vec<String>,
}
impl<'a> ViolationsBuilder<'a> {
fn new(violations: &'a mut Violations) -> Self {
Self {
violations,
current_path: Vec::new(),
}
}
fn add(&mut self, violation: Violation) {
self.violations
.violations
.push((self.current_path.clone(), violation));
}
fn push(&mut self, segment: String) {
self.current_path.push(segment);
}
fn pop(&mut self) {
self.current_path.pop();
}
}
impl Visitor for SettingsSchemaValidator {
fn visit_schema_object(&mut self, schema: &mut SchemaObject) {
let mut violations = Violations {
schema: schema.clone(),
violations: Vec::new(),
};
let mut builder = ViolationsBuilder::new(&mut violations);
self.validate_schema_object(&mut builder, schema);
if !violations.violations.is_empty() {
self.objects.push(violations);
}
schemars::visit::visit_schema_object(self, schema)
}
}
pub fn run_validate_schema(args: SchemaValidationArgs) -> Result<()> {
let contents = std::fs::read_to_string(args.path)?;
let mut schema: RootSchema = serde_json::from_str(&contents)?;
let mut validator = SettingsSchemaValidator::default();
validator.visit_root_schema(&mut schema);
let total_violations: usize = validator.objects.iter().map(|v| v.violations.len()).sum();
println!("Total violations found: {}", total_violations);
for violation in validator.objects.iter().flat_map(|v| &v.violations) {
let path_str = if violation.0.is_empty() {
"<root>".to_string()
} else {
violation.0.join(".")
};
println!("{}: {:?}", path_str, violation.1);
}
Ok(())
}