diff --git a/Cargo.lock b/Cargo.lock index 3531ac8877..0a544dcf65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6819,7 +6819,6 @@ dependencies = [ "regex", "rope", "rust-embed", - "schemars", "serde", "serde_json", "settings", diff --git a/crates/languages/Cargo.toml b/crates/languages/Cargo.toml index fffe069855..951423056e 100644 --- a/crates/languages/Cargo.toml +++ b/crates/languages/Cargo.toml @@ -56,7 +56,6 @@ project.workspace = true regex.workspace = true rope.workspace = true rust-embed.workspace = true -schemars.workspace = true serde.workspace = true serde_json.workspace = true settings.workspace = true diff --git a/crates/languages/src/json.rs b/crates/languages/src/json.rs index 07a28d6abd..6370b4c414 100644 --- a/crates/languages/src/json.rs +++ b/crates/languages/src/json.rs @@ -10,7 +10,6 @@ use language::{LanguageRegistry, LanguageToolchainStore, LspAdapter, LspAdapterD use lsp::{LanguageServerBinary, LanguageServerName}; use node_runtime::NodeRuntime; use project::{lsp_store::language_server_settings, ContextProviderWithTasks}; -use schemars::gen::SchemaSettings; use serde_json::{json, Value}; use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore}; use smol::{ @@ -76,6 +75,7 @@ impl JsonLspAdapter { } fn get_workspace_config(language_names: Vec, cx: &mut AppContext) -> Value { + let keymap_schema = KeymapFile::generate_json_schema_for_registered_actions(cx); let font_names = &cx.text_system().all_font_names(); let settings_schema = cx.global::().json_schema( &SettingsJsonSchemaParams { @@ -115,7 +115,7 @@ impl JsonLspAdapter { }, { "fileMatch": [schema_file_match(paths::keymap_file())], - "schema": Self::generate_keymap_schema(cx), + "schema": keymap_schema, }, { "fileMatch": [ @@ -129,16 +129,6 @@ impl JsonLspAdapter { } }) } - - fn generate_keymap_schema(cx: &mut AppContext) -> Value { - let mut generator = SchemaSettings::draft07() - .with(|settings| settings.option_add_null_type = false) - .into_generator(); - - let action_schemas = cx.action_schemas(&mut generator); - let deprecations = cx.action_deprecations(); - KeymapFile::generate_json_schema(generator, action_schemas, deprecations) - } } #[async_trait(?Send)] diff --git a/crates/settings/src/keymap_file.rs b/crates/settings/src/keymap_file.rs index 9b8a17c23e..e939bcef15 100644 --- a/crates/settings/src/keymap_file.rs +++ b/crates/settings/src/keymap_file.rs @@ -3,7 +3,7 @@ use anyhow::{anyhow, Context, Result}; use collections::{BTreeMap, HashMap}; use gpui::{Action, AppContext, KeyBinding, SharedString}; use schemars::{ - gen::SchemaGenerator, + gen::{SchemaGenerator, SchemaSettings}, schema::{ArrayValidation, InstanceType, Metadata, Schema, SchemaObject, SubschemaValidation}, JsonSchema, }; @@ -139,7 +139,17 @@ impl KeymapFile { Ok(()) } - pub fn generate_json_schema( + pub fn generate_json_schema_for_registered_actions(cx: &mut AppContext) -> Value { + let mut generator = SchemaSettings::draft07() + .with(|settings| settings.option_add_null_type = false) + .into_generator(); + + let action_schemas = cx.action_schemas(&mut generator); + let deprecations = cx.action_deprecations(); + KeymapFile::generate_json_schema(generator, action_schemas, deprecations) + } + + fn generate_json_schema( generator: SchemaGenerator, action_schemas: Vec<(SharedString, Option)>, deprecations: &HashMap, diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 4e8dd1bcba..28eb82daa0 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -3502,6 +3502,73 @@ mod tests { assert_key_bindings_for(workspace.into(), cx, vec![("6", &Deploy)], line!()); } + #[gpui::test] + async fn test_generate_keymap_json_schema_for_registered_actions( + cx: &mut gpui::TestAppContext, + ) { + init_keymap_test(cx); + cx.update(|cx| { + // Make sure it doesn't panic. + KeymapFile::generate_json_schema_for_registered_actions(cx); + }); + } + + /// Actions that don't build from empty input won't work from command palette invocation. + #[gpui::test] + async fn test_actions_build_with_empty_input(cx: &mut gpui::TestAppContext) { + init_keymap_test(cx); + cx.update(|cx| { + let all_actions = cx.all_action_names(); + let mut failing_names = Vec::new(); + let mut errors = Vec::new(); + for action in all_actions { + match action.to_string().as_str() { + "vim::FindCommand" + | "vim::Literal" + | "vim::ResizePane" + | "vim::SwitchMode" + | "vim::PushOperator" + | "vim::Number" + | "vim::SelectRegister" + | "terminal::SendText" + | "terminal::SendKeystroke" + | "app_menu::OpenApplicationMenu" + | "app_menu::NavigateApplicationMenuInDirection" + | "picker::ConfirmInput" + | "editor::HandleInput" + | "editor::FoldAtLevel" + | "pane::ActivateItem" + | "workspace::ActivatePane" + | "workspace::ActivatePaneInDirection" + | "workspace::MoveItemToPane" + | "workspace::MoveItemToPaneInDirection" + | "workspace::OpenTerminal" + | "workspace::SwapPaneInDirection" + | "workspace::SendKeystrokes" + | "zed::OpenBrowser" + | "zed::OpenZedUrl" => {} + _ => { + let result = cx.build_action(action, None); + match &result { + Ok(_) => {} + Err(err) => { + failing_names.push(action); + errors.push(format!("{action} failed to build: {err:?}")); + } + } + } + } + } + if errors.len() > 0 { + panic!( + "Failed to build actions using {{}} as input: {:?}. Errors:\n{}", + failing_names, + errors.join("\n") + ); + } + }); + } + #[gpui::test] fn test_bundled_settings_and_themes(cx: &mut AppContext) { cx.text_system()