Improve keymap json schema (#23044)

Also:

* Adds `impl_internal_actions!` for deriving the `Action` trait without
registering.

* Removes some deserializers that immediately fail in favor of
`#[serde(skip)]` on fields where they were used. This also omits them
from the schema.

Release Notes:

- Keymap settings file now has more JSON schema information to inform
`json-language-server` completions and info, particularly for actions
that take input.
This commit is contained in:
Michael Sloan 2025-01-12 19:34:35 -07:00 committed by GitHub
parent 4c50201036
commit 6aba3950d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 506 additions and 283 deletions

6
Cargo.lock generated
View file

@ -6817,6 +6817,7 @@ dependencies = [
"regex",
"rope",
"rust-embed",
"schemars",
"serde",
"serde_json",
"settings",
@ -9332,6 +9333,7 @@ dependencies = [
"env_logger 0.11.6",
"gpui",
"menu",
"schemars",
"serde",
"serde_json",
"ui",
@ -11278,6 +11280,7 @@ dependencies = [
"language",
"menu",
"project",
"schemars",
"serde",
"serde_json",
"settings",
@ -12659,6 +12662,7 @@ dependencies = [
"menu",
"picker",
"project",
"schemars",
"serde",
"serde_json",
"settings",
@ -12852,6 +12856,7 @@ dependencies = [
"language",
"project",
"rand 0.8.5",
"schemars",
"search",
"serde",
"serde_json",
@ -13186,6 +13191,7 @@ dependencies = [
"project",
"remote",
"rpc",
"schemars",
"serde",
"settings",
"smallvec",

View file

@ -26,7 +26,7 @@ pub use context::*;
pub use context_store::*;
use feature_flags::FeatureFlagAppExt;
use fs::Fs;
use gpui::impl_actions;
use gpui::impl_internal_actions;
use gpui::{actions, AppContext, Global, SharedString, UpdateGlobal};
pub(crate) use inline_assistant::*;
use language_model::{
@ -74,13 +74,13 @@ actions!(
]
);
#[derive(PartialEq, Clone, Deserialize)]
#[derive(PartialEq, Clone)]
pub enum InsertDraggedFiles {
ProjectPaths(Vec<PathBuf>),
ExternalFiles(Vec<PathBuf>),
}
impl_actions!(assistant, [InsertDraggedFiles]);
impl_internal_actions!(assistant, [InsertDraggedFiles]);
const DEFAULT_CONTEXT_LINES: usize = 50;

View file

@ -1,82 +1,84 @@
//! This module contains all actions supported by [`Editor`].
use super::*;
use gpui::{action_aliases, action_as};
use schemars::JsonSchema;
use util::serde::default_true;
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct SelectNext {
#[serde(default)]
pub replace_newest: bool,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct SelectPrevious {
#[serde(default)]
pub replace_newest: bool,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct MoveToBeginningOfLine {
#[serde(default = "default_true")]
pub stop_at_soft_wraps: bool,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct SelectToBeginningOfLine {
#[serde(default)]
pub(super) stop_at_soft_wraps: bool,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct MovePageUp {
#[serde(default)]
pub(super) center_cursor: bool,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct MovePageDown {
#[serde(default)]
pub(super) center_cursor: bool,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct MoveToEndOfLine {
#[serde(default = "default_true")]
pub stop_at_soft_wraps: bool,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct SelectToEndOfLine {
#[serde(default)]
pub(super) stop_at_soft_wraps: bool,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct ToggleCodeActions {
// Display row from which the action was deployed.
#[serde(default)]
#[serde(skip)]
pub deployed_from_indicator: Option<DisplayRow>,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct ConfirmCompletion {
#[serde(default)]
pub item_ix: Option<usize>,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct ComposeCompletion {
#[serde(default)]
pub item_ix: Option<usize>,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct ConfirmCodeAction {
#[serde(default)]
pub item_ix: Option<usize>,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct ToggleComments {
#[serde(default)]
pub advance_downwards: bool,
@ -84,84 +86,87 @@ pub struct ToggleComments {
pub ignore_indent: bool,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct FoldAt {
#[serde(skip)]
pub buffer_row: MultiBufferRow,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct UnfoldAt {
#[serde(skip)]
pub buffer_row: MultiBufferRow,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct MoveUpByLines {
#[serde(default)]
pub(super) lines: u32,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct MoveDownByLines {
#[serde(default)]
pub(super) lines: u32,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct SelectUpByLines {
#[serde(default)]
pub(super) lines: u32,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct SelectDownByLines {
#[serde(default)]
pub(super) lines: u32,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct ExpandExcerpts {
#[serde(default)]
pub(super) lines: u32,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct ExpandExcerptsUp {
#[serde(default)]
pub(super) lines: u32,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct ExpandExcerptsDown {
#[serde(default)]
pub(super) lines: u32,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct ShowCompletions {
#[serde(default)]
pub(super) trigger: Option<String>,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct HandleInput(pub String);
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct DeleteToNextWordEnd {
#[serde(default)]
pub ignore_newlines: bool,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct DeleteToPreviousWordStart {
#[serde(default)]
pub ignore_newlines: bool,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct FoldAtLevel {
pub level: u32,
}
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct SpawnNearestTask {
#[serde(default)]
pub reveal: task::RevealStrategy,

View file

@ -63,6 +63,16 @@ pub trait Action: 'static + Send {
where
Self: Sized;
/// Optional JSON schema for the action's input data.
fn action_json_schema(
_: &mut schemars::gen::SchemaGenerator,
) -> Option<schemars::schema::Schema>
where
Self: Sized,
{
None
}
/// A list of alternate, deprecated names for this action.
fn deprecated_aliases() -> &'static [&'static str]
where
@ -90,16 +100,16 @@ impl dyn Action {
type ActionBuilder = fn(json: serde_json::Value) -> anyhow::Result<Box<dyn Action>>;
pub(crate) struct ActionRegistry {
builders_by_name: HashMap<SharedString, ActionBuilder>,
by_name: HashMap<SharedString, ActionData>,
names_by_type_id: HashMap<TypeId, SharedString>,
all_names: Vec<SharedString>, // So we can return a static slice.
deprecations: Vec<(SharedString, SharedString)>,
deprecations: HashMap<SharedString, SharedString>,
}
impl Default for ActionRegistry {
fn default() -> Self {
let mut this = ActionRegistry {
builders_by_name: Default::default(),
by_name: Default::default(),
names_by_type_id: Default::default(),
all_names: Default::default(),
deprecations: Default::default(),
@ -111,19 +121,25 @@ impl Default for ActionRegistry {
}
}
/// This type must be public so that our macros can build it in other crates.
/// But this is an implementation detail and should not be used directly.
#[doc(hidden)]
pub type MacroActionBuilder = fn() -> ActionData;
struct ActionData {
pub build: ActionBuilder,
pub json_schema: fn(&mut schemars::gen::SchemaGenerator) -> Option<schemars::schema::Schema>,
}
/// This type must be public so that our macros can build it in other crates.
/// But this is an implementation detail and should not be used directly.
#[doc(hidden)]
pub struct ActionData {
pub type MacroActionBuilder = fn() -> MacroActionData;
/// This type must be public so that our macros can build it in other crates.
/// But this is an implementation detail and should not be used directly.
#[doc(hidden)]
pub struct MacroActionData {
pub name: &'static str,
pub aliases: &'static [&'static str],
pub type_id: TypeId,
pub build: ActionBuilder,
pub json_schema: fn(&mut schemars::gen::SchemaGenerator) -> Option<schemars::schema::Schema>,
}
/// This constant must be public to be accessible from other crates.
@ -143,20 +159,35 @@ impl ActionRegistry {
#[cfg(test)]
pub(crate) fn load_action<A: Action>(&mut self) {
self.insert_action(ActionData {
self.insert_action(MacroActionData {
name: A::debug_name(),
aliases: A::deprecated_aliases(),
type_id: TypeId::of::<A>(),
build: A::build,
json_schema: A::action_json_schema,
});
}
fn insert_action(&mut self, action: ActionData) {
fn insert_action(&mut self, action: MacroActionData) {
let name: SharedString = action.name.into();
self.builders_by_name.insert(name.clone(), action.build);
self.by_name.insert(
name.clone(),
ActionData {
build: action.build,
json_schema: action.json_schema,
},
);
for &alias in action.aliases {
self.builders_by_name.insert(alias.into(), action.build);
self.deprecations.push((alias.into(), name.clone()));
let alias: SharedString = alias.into();
self.by_name.insert(
alias.clone(),
ActionData {
build: action.build,
json_schema: action.json_schema,
},
);
self.deprecations.insert(alias.clone(), name.clone());
self.all_names.push(alias);
}
self.names_by_type_id.insert(action.type_id, name.clone());
self.all_names.push(name);
@ -180,9 +211,10 @@ impl ActionRegistry {
params: Option<serde_json::Value>,
) -> Result<Box<dyn Action>> {
let build_action = self
.builders_by_name
.by_name
.get(name)
.ok_or_else(|| anyhow!("no action type registered for {}", name))?;
.ok_or_else(|| anyhow!("No action type registered for {}", name))?
.build;
(build_action)(params.unwrap_or_else(|| json!({})))
.with_context(|| format!("Attempting to build action {}", name))
}
@ -191,12 +223,30 @@ impl ActionRegistry {
self.all_names.as_slice()
}
pub fn action_deprecations(&self) -> &[(SharedString, SharedString)] {
self.deprecations.as_slice()
pub fn action_schemas(
&self,
generator: &mut schemars::gen::SchemaGenerator,
) -> Vec<(SharedString, Option<schemars::schema::Schema>)> {
// Use the order from all_names so that the resulting schema has sensible order.
self.all_names
.iter()
.map(|name| {
let action_data = self
.by_name
.get(name)
.expect("All actions in all_names should be registered");
(name.clone(), (action_data.json_schema)(generator))
})
.collect::<Vec<_>>()
}
pub fn action_deprecations(&self) -> &HashMap<SharedString, SharedString> {
&self.deprecations
}
}
/// Defines unit structs that can be used as actions.
/// Defines and registers unit structs that can be used as actions.
///
/// To use more complex data types as actions, use `impl_actions!`
#[macro_export]
macro_rules! actions {
@ -211,6 +261,11 @@ macro_rules! actions {
gpui::__impl_action!($namespace, $name, $name,
fn build(_: gpui::private::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>> {
Ok(Box::new(Self))
},
fn action_json_schema(
_: &mut gpui::private::schemars::gen::SchemaGenerator,
) -> Option<gpui::private::schemars::schema::Schema> {
None
}
);
@ -219,11 +274,10 @@ macro_rules! actions {
};
}
/// Defines a unit struct that can be used as an actions, with a name
/// that differs from it's type name.
/// Defines and registers a unit struct that can be used as an actions, with a name that differs
/// from it's type name.
///
/// To use more complex data types as actions, and rename them use
/// `impl_action_as!`
/// To use more complex data types as actions, and rename them use `impl_action_as!`
#[macro_export]
macro_rules! action_as {
($namespace:path, $name:ident as $visual_name:ident) => {
@ -241,6 +295,11 @@ macro_rules! action_as {
_: gpui::private::serde_json::Value,
) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>> {
Ok(Box::new(Self))
},
fn action_json_schema(
generator: &mut gpui::private::schemars::gen::SchemaGenerator,
) -> Option<gpui::private::schemars::schema::Schema> {
None
}
);
@ -248,7 +307,7 @@ macro_rules! action_as {
};
}
/// Defines a unit struct that can be used as an action, with some deprecated aliases.
/// Defines and registers a unit struct that can be used as an action, with some deprecated aliases.
#[macro_export]
macro_rules! action_aliases {
($namespace:path, $name:ident, [$($alias:ident),* $(,)?]) => {
@ -261,6 +320,7 @@ macro_rules! action_aliases {
::std::default::Default,
::std::fmt::Debug,
gpui::private::serde_derive::Deserialize,
gpui::private::schemars::JsonSchema,
)]
#[serde(crate = "gpui::private::serde")]
pub struct $name;
@ -274,6 +334,12 @@ macro_rules! action_aliases {
) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>> {
Ok(Box::new(Self))
},
fn action_json_schema(
generator: &mut gpui::private::schemars::gen::SchemaGenerator,
) -> Option<gpui::private::schemars::schema::Schema> {
None
},
fn deprecated_aliases() -> &'static [&'static str] {
&[
$(concat!(stringify!($namespace), "::", stringify!($alias))),*
@ -285,7 +351,11 @@ macro_rules! action_aliases {
};
}
/// Implements the Action trait for any struct that implements Clone, Default, PartialEq, and serde_deserialize::Deserialize
/// Registers the action and implements the Action trait for any struct that implements Clone,
/// Default, PartialEq, serde_deserialize::Deserialize, and schemars::JsonSchema.
///
/// Fields and variants that don't make sense for user configuration should be annotated with
/// #[serde(skip)].
#[macro_export]
macro_rules! impl_actions {
($namespace:path, [ $($name:ident),* $(,)? ]) => {
@ -293,6 +363,13 @@ macro_rules! impl_actions {
gpui::__impl_action!($namespace, $name, $name,
fn build(value: gpui::private::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>> {
Ok(std::boxed::Box::new(gpui::private::serde_json::from_value::<Self>(value)?))
},
fn action_json_schema(
generator: &mut gpui::private::schemars::gen::SchemaGenerator,
) -> Option<gpui::private::schemars::schema::Schema> {
Some(<Self as gpui::private::schemars::JsonSchema>::json_schema(
generator,
))
}
);
@ -301,8 +378,41 @@ macro_rules! impl_actions {
};
}
/// Implements the Action trait for a struct that implements Clone, Default, PartialEq, and serde_deserialize::Deserialize
/// Allows you to rename the action visually, without changing the struct's name
/// Implements the Action trait for internal action structs that implement Clone, Default,
/// PartialEq. The purpose of this is to conveniently define values that can be passed in `dyn
/// Action`.
///
/// These actions are internal and so are not registered and do not support deserialization.
#[macro_export]
macro_rules! impl_internal_actions {
($namespace:path, [ $($name:ident),* $(,)? ]) => {
$(
gpui::__impl_action!($namespace, $name, $name,
fn build(value: gpui::private::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>> {
gpui::Result::Err(gpui::private::anyhow::anyhow!(
concat!(
stringify!($namespace),
"::",
stringify!($visual_name),
" is an internal action, so cannot be built from JSON."
)))
},
fn action_json_schema(
generator: &mut gpui::private::schemars::gen::SchemaGenerator,
) -> Option<gpui::private::schemars::schema::Schema> {
None
}
);
)*
};
}
/// Implements the Action trait for a struct that implements Clone, Default, PartialEq, and
/// serde_deserialize::Deserialize. Allows you to rename the action visually, without changing the
/// struct's name.
///
/// Fields and variants that don't make sense for user configuration should be annotated with
/// #[serde(skip)].
#[macro_export]
macro_rules! impl_action_as {
($namespace:path, $name:ident as $visual_name:tt ) => {
@ -316,6 +426,13 @@ macro_rules! impl_action_as {
Ok(std::boxed::Box::new(
gpui::private::serde_json::from_value::<Self>(value)?,
))
},
fn action_json_schema(
generator: &mut gpui::private::schemars::gen::SchemaGenerator,
) -> Option<gpui::private::schemars::schema::Schema> {
Some(<Self as gpui::private::schemars::JsonSchema>::json_schema(
generator,
))
}
);

View file

@ -23,7 +23,7 @@ use parking_lot::RwLock;
use slotmap::SlotMap;
pub use async_context::*;
use collections::{FxHashMap, FxHashSet, VecDeque};
use collections::{FxHashMap, FxHashSet, HashMap, VecDeque};
pub use entity_map::*;
use http_client::HttpClient;
pub use model_context::*;
@ -1218,16 +1218,22 @@ impl AppContext {
self.actions.build_action(name, data)
}
/// Get a list of all action names that have been registered.
/// in the application. Note that registration only allows for
/// actions to be built dynamically, and is unrelated to binding
/// actions in the element tree.
/// Get all action names that have been registered. Note that registration only allows for
/// actions to be built dynamically, and is unrelated to binding actions in the element tree.
pub fn all_action_names(&self) -> &[SharedString] {
self.actions.all_action_names()
}
/// Get all non-internal actions that have been registered, along with their schemas.
pub fn action_schemas(
&self,
generator: &mut schemars::gen::SchemaGenerator,
) -> Vec<(SharedString, Option<schemars::schema::Schema>)> {
self.actions.action_schemas(generator)
}
/// Get a list of all deprecated action aliases and their canonical names.
pub fn action_deprecations(&self) -> &[(SharedString, SharedString)] {
pub fn action_deprecations(&self) -> &HashMap<SharedString, SharedString> {
self.actions.action_deprecations()
}

View file

@ -102,7 +102,9 @@ mod window;
/// Do not touch, here be dragons for use by gpui_macros and such.
#[doc(hidden)]
pub mod private {
pub use anyhow;
pub use linkme;
pub use schemars;
pub use serde;
pub use serde_derive;
pub use serde_json;

View file

@ -1,12 +1,13 @@
use gpui::{actions, impl_actions};
use gpui_macros::register_action;
use schemars::JsonSchema;
use serde_derive::Deserialize;
#[test]
fn test_action_macros() {
actions!(test, [TestAction]);
#[derive(PartialEq, Clone, Deserialize)]
#[derive(PartialEq, Clone, Deserialize, JsonSchema)]
struct AnotherTestAction;
impl_actions!(test, [AnotherTestAction]);

View file

@ -29,12 +29,13 @@ pub(crate) fn register_action(type_name: &Ident) -> proc_macro2::TokenStream {
fn __autogenerated() {
/// This is an auto generated function, do not use.
#[doc(hidden)]
fn #action_builder_fn_name() -> gpui::ActionData {
gpui::ActionData {
fn #action_builder_fn_name() -> gpui::MacroActionData {
gpui::MacroActionData {
name: <#type_name as gpui::Action>::debug_name(),
aliases: <#type_name as gpui::Action>::deprecated_aliases(),
type_id: ::std::any::TypeId::of::<#type_name>(),
build: <#type_name as gpui::Action>::build,
json_schema: <#type_name as gpui::Action>::action_json_schema,
}
}
#[doc(hidden)]

View file

@ -662,8 +662,7 @@ pub struct LanguageConfigOverride {
pub line_comments: Override<Vec<Arc<str>>>,
#[serde(default)]
pub block_comment: Override<(Arc<str>, Arc<str>)>,
#[serde(skip_deserializing)]
#[schemars(skip)]
#[serde(skip)]
pub disabled_bracket_ixs: Vec<u16>,
#[serde(default)]
pub word_characters: Override<HashSet<char>>,
@ -776,7 +775,7 @@ pub struct BracketPairConfig {
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]`
#[schemars(skip)]
#[serde(skip)]
pub disabled_scopes_by_bracket_ix: Vec<Vec<String>>,
}

View file

@ -56,6 +56,7 @@ 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

View file

@ -10,6 +10,7 @@ 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::{
@ -75,9 +76,6 @@ impl JsonLspAdapter {
}
fn get_workspace_config(language_names: Vec<String>, cx: &mut AppContext) -> Value {
let action_names = cx.all_action_names();
let deprecations = cx.action_deprecations();
let font_names = &cx.text_system().all_font_names();
let settings_schema = cx.global::<SettingsStore>().json_schema(
&SettingsJsonSchemaParams {
@ -117,7 +115,7 @@ impl JsonLspAdapter {
},
{
"fileMatch": [schema_file_match(paths::keymap_file())],
"schema": KeymapFile::generate_json_schema(action_names, deprecations),
"schema": Self::generate_keymap_schema(cx),
},
{
"fileMatch": [
@ -131,6 +129,16 @@ 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)]

View file

@ -20,6 +20,7 @@ anyhow.workspace = true
editor.workspace = true
gpui.workspace = true
menu.workspace = true
schemars.workspace = true
serde.workspace = true
ui.workspace = true
workspace.workspace = true

View file

@ -7,6 +7,7 @@ use gpui::{
ViewContext, WindowContext,
};
use head::Head;
use schemars::JsonSchema;
use serde::Deserialize;
use std::{sync::Arc, time::Duration};
use ui::{prelude::*, v_flex, Color, Divider, Label, ListItem, ListItemSpacing};
@ -24,7 +25,7 @@ actions!(picker, [ConfirmCompletion]);
/// ConfirmInput is an alternative editor action which - instead of selecting active picker entry - treats pickers editor input literally,
/// performing some kind of action on it.
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(Clone, PartialEq, Deserialize, JsonSchema, Default)]
pub struct ConfirmInput {
pub secondary: bool,
}

View file

@ -1,10 +1,10 @@
mod project_panel_settings;
mod utils;
use anyhow::{anyhow, Context as _, Result};
use client::{ErrorCode, ErrorExt};
use language::DiagnosticSeverity;
use settings::{Settings, SettingsStore};
use collections::{hash_map, BTreeSet, HashMap};
use command_palette_hooks::CommandPaletteFilter;
use db::kvp::KEY_VALUE_STORE;
use editor::{
items::{
@ -15,10 +15,6 @@ use editor::{
Editor, EditorEvent, EditorSettings, ShowScrollbar,
};
use file_icons::FileIcons;
use anyhow::{anyhow, Context as _, Result};
use collections::{hash_map, BTreeSet, HashMap};
use command_palette_hooks::CommandPaletteFilter;
use git::repository::GitFileStatus;
use gpui::{
actions, anchored, deferred, div, impl_actions, point, px, size, uniform_list, Action,
@ -30,6 +26,7 @@ use gpui::{
VisualContext as _, WeakView, WindowContext,
};
use indexmap::IndexMap;
use language::DiagnosticSeverity;
use menu::{Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev};
use project::{
relativize_path, Entry, EntryKind, Fs, Project, ProjectEntryId, ProjectPath, Worktree,
@ -38,7 +35,9 @@ use project::{
use project_panel_settings::{
ProjectPanelDockPosition, ProjectPanelSettings, ShowDiagnostics, ShowIndentGuides,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
use smallvec::SmallVec;
use std::any::TypeId;
use std::{
@ -152,13 +151,13 @@ struct EntryDetails {
canonical_path: Option<Box<Path>>,
}
#[derive(PartialEq, Clone, Default, Debug, Deserialize)]
#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema)]
struct Delete {
#[serde(default)]
pub skip_prompt: bool,
}
#[derive(PartialEq, Clone, Default, Debug, Deserialize)]
#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema)]
struct Trash {
#[serde(default)]
pub skip_prompt: bool,

View file

@ -31,6 +31,7 @@ gpui.workspace = true
language.workspace = true
menu.workspace = true
project.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true

View file

@ -22,6 +22,7 @@ use project::{
search::SearchQuery,
search_history::{SearchHistory, SearchHistoryCursor},
};
use schemars::JsonSchema;
use serde::Deserialize;
use settings::Settings;
use std::sync::Arc;
@ -43,7 +44,7 @@ use registrar::{ForDeployed, ForDismissed, SearchActionsRegistrar, WithResults};
const MAX_BUFFER_SEARCH_HISTORY_SIZE: usize = 50;
#[derive(PartialEq, Clone, Deserialize)]
#[derive(PartialEq, Clone, Deserialize, JsonSchema)]
pub struct Deploy {
#[serde(default = "util::serde::default_true")]
pub focus: bool,

View file

@ -1,11 +1,11 @@
use crate::{settings_store::parse_json_with_comments, SettingsAssets};
use anyhow::{anyhow, Context, Result};
use collections::BTreeMap;
use collections::{BTreeMap, HashMap};
use gpui::{Action, AppContext, KeyBinding, SharedString};
use schemars::{
gen::{SchemaGenerator, SchemaSettings},
schema::{InstanceType, Schema, SchemaObject, SingleOrVec, SubschemaValidation},
JsonSchema, Map,
gen::SchemaGenerator,
schema::{ArrayValidation, InstanceType, Metadata, Schema, SchemaObject, SubschemaValidation},
JsonSchema,
};
use serde::Deserialize;
use serde_json::Value;
@ -140,55 +140,117 @@ impl KeymapFile {
}
pub fn generate_json_schema(
action_names: &[SharedString],
deprecations: &[(SharedString, SharedString)],
generator: SchemaGenerator,
action_schemas: Vec<(SharedString, Option<Schema>)>,
deprecations: &HashMap<SharedString, SharedString>,
) -> serde_json::Value {
let mut root_schema = SchemaSettings::draft07()
.with(|settings| settings.option_add_null_type = false)
.into_generator()
.into_root_schema_for::<KeymapFile>();
let mut alternatives = vec![
Schema::Object(SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
enum_values: Some(
action_names
.iter()
.map(|name| Value::String(name.to_string()))
.collect(),
),
..Default::default()
}),
Schema::Object(SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Array))),
..Default::default()
}),
Schema::Object(SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Null))),
..Default::default()
}),
];
for (old, new) in deprecations {
alternatives.push(Schema::Object(SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
const_value: Some(Value::String(old.to_string())),
extensions: Map::from_iter([(
// deprecationMessage is not part of the JSON Schema spec,
// but json-language-server recognizes it.
"deprecationMessage".to_owned(),
format!("Deprecated, use {new}").into(),
)]),
..Default::default()
}));
fn set<I, O>(input: I) -> Option<O>
where
I: Into<O>,
{
Some(input.into())
}
let action_schema = Schema::Object(SchemaObject {
subschemas: Some(Box::new(SubschemaValidation {
one_of: Some(alternatives),
..Default::default()
})),
..Default::default()
});
fn add_deprecation_notice(schema_object: &mut SchemaObject, new_name: &SharedString) {
schema_object.extensions.insert(
// deprecationMessage is not part of the JSON Schema spec,
// but json-language-server recognizes it.
"deprecationMessage".to_owned(),
format!("Deprecated, use {new_name}").into(),
);
}
let empty_object: SchemaObject = SchemaObject {
instance_type: set(InstanceType::Object),
..Default::default()
};
let mut keymap_action_alternatives = Vec::new();
for (name, action_schema) in action_schemas.iter() {
let schema = if let Some(Schema::Object(schema)) = action_schema {
Some(schema.clone())
} else {
None
};
// If the type has a description, also apply it to the value. Ideally it would be
// removed and applied to the overall array, but `json-language-server` does not show
// these descriptions.
let description = schema.as_ref().and_then(|schema| {
schema
.metadata
.as_ref()
.and_then(|metadata| metadata.description.as_ref())
});
let mut matches_action_name = SchemaObject {
const_value: Some(Value::String(name.to_string())),
..Default::default()
};
if let Some(description) = description {
matches_action_name.metadata = set(Metadata {
description: Some(description.clone()),
..Default::default()
});
}
// Add an alternative for plain action names.
let deprecation = deprecations.get(name);
let mut plain_action = SchemaObject {
instance_type: set(InstanceType::String),
const_value: Some(Value::String(name.to_string())),
..Default::default()
};
if let Some(new_name) = deprecation {
add_deprecation_notice(&mut plain_action, new_name);
}
keymap_action_alternatives.push(plain_action.into());
// When all fields are skipped or an empty struct is added with impl_actions! /
// impl_actions_as! an empty struct is produced. The action should be invoked without
// data in this case.
if let Some(schema) = schema {
if schema != empty_object {
let mut action_with_data = SchemaObject {
instance_type: set(InstanceType::Array),
array: Some(
ArrayValidation {
items: set(vec![matches_action_name.into(), schema.into()]),
min_items: Some(2),
max_items: Some(2),
..Default::default()
}
.into(),
),
..Default::default()
};
if let Some(new_name) = deprecation {
add_deprecation_notice(&mut action_with_data, new_name);
}
keymap_action_alternatives.push(action_with_data.into());
}
}
}
// 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(),
);
let action_schema = SchemaObject {
subschemas: set(SubschemaValidation {
one_of: Some(keymap_action_alternatives),
..Default::default()
}),
..Default::default()
}
.into();
let mut root_schema = generator.into_root_schema_for::<KeymapFile>();
root_schema
.definitions
.insert("KeymapAction".to_owned(), action_schema);

View file

@ -19,6 +19,7 @@ gpui.workspace = true
menu.workspace = true
picker.workspace = true
project.workspace = true
schemars.workspace = true
serde.workspace = true
settings.workspace = true
ui.workspace = true

View file

@ -10,6 +10,7 @@ use gpui::{
};
use picker::{Picker, PickerDelegate};
use project::Project;
use schemars::JsonSchema;
use serde::Deserialize;
use settings::Settings;
use std::sync::Arc;
@ -23,7 +24,7 @@ use workspace::{
const PANEL_WIDTH_REMS: f32 = 28.;
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, JsonSchema, Default)]
pub struct Toggle {
#[serde(default)]
pub select_last: bool,

View file

@ -29,6 +29,7 @@ itertools.workspace = true
language.workspace = true
project.workspace = true
task.workspace = true
schemars.workspace = true
search.workspace = true
serde.workspace = true
serde_json.workspace = true

View file

@ -15,6 +15,7 @@ use gpui::{
use language::Bias;
use persistence::TERMINAL_DB;
use project::{search::SearchQuery, terminals::TerminalKind, Fs, Metadata, Project};
use schemars::JsonSchema;
use terminal::{
alacritty_terminal::{
index::Point,
@ -66,14 +67,14 @@ const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
const GIT_DIFF_PATH_PREFIXES: &[char] = &['a', 'b'];
///Event to transmit the scroll from the element to the view
/// Event to transmit the scroll from the element to the view
#[derive(Clone, Debug, PartialEq)]
pub struct ScrollTerminal(pub i32);
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, JsonSchema, PartialEq)]
pub struct SendText(String);
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, JsonSchema, PartialEq)]
pub struct SendKeystroke(String);
impl_actions!(terminal, [SendText, SendKeystroke]);

View file

@ -36,6 +36,7 @@ notifications.workspace = true
project.workspace = true
remote.workspace = true
rpc.workspace = true
schemars.workspace = true
serde.workspace = true
settings.workspace = true
smallvec.workspace = true

View file

@ -1,17 +1,18 @@
use gpui::{impl_actions, OwnedMenu, OwnedMenuItem, View};
use schemars::JsonSchema;
use serde::Deserialize;
use smallvec::SmallVec;
use ui::{prelude::*, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip};
impl_actions!(
app_menu,
[OpenApplicationMenu, NavigateApplicationMenuInDirection,]
[OpenApplicationMenu, NavigateApplicationMenuInDirection]
);
#[derive(Clone, Deserialize, PartialEq, Default)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq, Default)]
pub struct OpenApplicationMenu(String);
#[derive(Clone, Deserialize, PartialEq, Default)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq, Default)]
pub struct NavigateApplicationMenuInDirection(String);
#[derive(Clone)]

View file

@ -1,11 +1,3 @@
use std::{
iter::Peekable,
ops::{Deref, Range},
str::Chars,
sync::OnceLock,
time::Instant,
};
use anyhow::{anyhow, Result};
use command_palette_hooks::CommandInterceptResult;
use editor::{
@ -13,12 +5,22 @@ use editor::{
display_map::ToDisplayPoint,
Bias, Editor, ToPoint,
};
use gpui::{actions, impl_actions, Action, AppContext, Global, ViewContext, WindowContext};
use gpui::{
actions, impl_internal_actions, Action, AppContext, Global, ViewContext, WindowContext,
};
use language::Point;
use multi_buffer::MultiBufferRow;
use regex::Regex;
use schemars::JsonSchema;
use search::{BufferSearchBar, SearchOptions};
use serde::Deserialize;
use std::{
iter::Peekable,
ops::{Deref, Range},
str::Chars,
sync::OnceLock,
time::Instant,
};
use util::ResultExt;
use workspace::{notifications::NotifyResultExt, SaveIntent};
@ -33,24 +35,24 @@ use crate::{
Vim,
};
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[derive(Clone, Debug, PartialEq)]
pub struct GoToLine {
range: CommandRange,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[derive(Clone, Debug, PartialEq)]
pub struct YankCommand {
range: CommandRange,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[derive(Clone, Debug, PartialEq)]
pub struct WithRange {
restore_selection: bool,
range: CommandRange,
action: WrappedAction,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[derive(Clone, Debug, PartialEq)]
pub struct WithCount {
count: u32,
action: WrappedAction,
@ -60,20 +62,11 @@ pub struct WithCount {
struct WrappedAction(Box<dyn Action>);
actions!(vim, [VisualCommand, CountCommand]);
impl_actions!(
impl_internal_actions!(
vim,
[GoToLine, YankCommand, WithRange, WithCount, OnMatchingLines]
);
impl<'de> Deserialize<'de> for WrappedAction {
fn deserialize<D>(_: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Err(serde::de::Error::custom("Cannot deserialize WrappedAction"))
}
}
impl PartialEq for WrappedAction {
fn eq(&self, other: &Self) -> bool {
self.0.partial_eq(&*other.0)
@ -423,7 +416,7 @@ impl VimCommand {
}
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq)]
enum Position {
Line { row: u32, offset: i32 },
Mark { name: char, offset: i32 },
@ -467,7 +460,7 @@ impl Position {
}
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct CommandRange {
start: Position,
end: Option<Position>,
@ -877,7 +870,7 @@ fn generate_positions(string: &str, query: &str) -> Vec<usize> {
positions
}
#[derive(Debug, PartialEq, Deserialize, Clone)]
#[derive(Debug, PartialEq, Clone)]
pub(crate) struct OnMatchingLines {
range: CommandRange,
search: String,

View file

@ -3,6 +3,7 @@ use std::sync::Arc;
use collections::HashMap;
use editor::Editor;
use gpui::{impl_actions, AppContext, Keystroke, KeystrokeEvent};
use schemars::JsonSchema;
use serde::Deserialize;
use settings::Settings;
use std::sync::LazyLock;
@ -12,7 +13,7 @@ use crate::{state::Operator, Vim, VimSettings};
mod default;
#[derive(PartialEq, Clone, Deserialize)]
#[derive(Debug, Clone, Deserialize, JsonSchema, PartialEq)]
struct Literal(String, char);
impl_actions!(vim, [Literal]);

View file

@ -9,6 +9,7 @@ use editor::{
use gpui::{actions, impl_actions, px, ViewContext};
use language::{CharKind, Point, Selection, SelectionGoal};
use multi_buffer::MultiBufferRow;
use schemars::JsonSchema;
use serde::Deserialize;
use std::ops::Range;
use workspace::searchable::Direction;
@ -139,105 +140,105 @@ pub enum Motion {
},
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
struct NextWordStart {
#[serde(default)]
ignore_punctuation: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
struct NextWordEnd {
#[serde(default)]
ignore_punctuation: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
struct PreviousWordStart {
#[serde(default)]
ignore_punctuation: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
struct PreviousWordEnd {
#[serde(default)]
ignore_punctuation: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct NextSubwordStart {
#[serde(default)]
pub(crate) ignore_punctuation: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct NextSubwordEnd {
#[serde(default)]
pub(crate) ignore_punctuation: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PreviousSubwordStart {
#[serde(default)]
pub(crate) ignore_punctuation: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PreviousSubwordEnd {
#[serde(default)]
pub(crate) ignore_punctuation: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Up {
#[serde(default)]
pub(crate) display_lines: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Down {
#[serde(default)]
pub(crate) display_lines: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
struct FirstNonWhitespace {
#[serde(default)]
display_lines: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
struct EndOfLine {
#[serde(default)]
display_lines: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct StartOfLine {
#[serde(default)]
pub(crate) display_lines: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
struct UnmatchedForward {
#[serde(default)]
char: char,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
struct UnmatchedBackward {
#[serde(default)]

View file

@ -1,20 +1,20 @@
use std::ops::Range;
use editor::{scroll::Autoscroll, Editor, MultiBufferSnapshot, ToOffset, ToPoint};
use gpui::{impl_actions, ViewContext};
use language::{Bias, Point};
use schemars::JsonSchema;
use serde::Deserialize;
use std::ops::Range;
use crate::{state::Mode, Vim};
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
struct Increment {
#[serde(default)]
step: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
struct Decrement {
#[serde(default)]

View file

@ -1,16 +1,16 @@
use std::cmp;
use editor::{display_map::ToDisplayPoint, movement, scroll::Autoscroll, DisplayPoint, RowExt};
use gpui::{impl_actions, ViewContext};
use language::{Bias, SelectionGoal};
use schemars::JsonSchema;
use serde::Deserialize;
use std::cmp;
use crate::{
state::{Mode, Register},
Vim,
};
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Paste {
#[serde(default)]

View file

@ -1,10 +1,10 @@
use std::{iter::Peekable, str::Chars, time::Duration};
use editor::Editor;
use gpui::{actions, impl_actions, ViewContext};
use gpui::{actions, impl_actions, impl_internal_actions, ViewContext};
use language::Point;
use schemars::JsonSchema;
use search::{buffer_search, BufferSearchBar, SearchOptions};
use serde_derive::Deserialize;
use std::{iter::Peekable, str::Chars, time::Duration};
use util::serde::default_true;
use workspace::{notifications::NotifyResultExt, searchable::Direction};
@ -15,7 +15,7 @@ use crate::{
Vim,
};
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct MoveToNext {
#[serde(default = "default_true")]
@ -26,7 +26,7 @@ pub(crate) struct MoveToNext {
regex: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct MoveToPrev {
#[serde(default = "default_true")]
@ -37,7 +37,7 @@ pub(crate) struct MoveToPrev {
regex: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq)]
pub(crate) struct Search {
#[serde(default)]
backwards: bool,
@ -45,19 +45,19 @@ pub(crate) struct Search {
regex: bool,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq)]
pub struct FindCommand {
pub query: String,
pub backwards: bool,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[derive(Clone, Debug, PartialEq)]
pub struct ReplaceCommand {
pub(crate) range: CommandRange,
pub(crate) replacement: Replacement,
}
#[derive(Debug, Default, PartialEq, Deserialize, Clone)]
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct Replacement {
search: String,
replacement: String,
@ -66,10 +66,8 @@ pub(crate) struct Replacement {
}
actions!(vim, [SearchSubmit, MoveToNextMatch, MoveToPrevMatch]);
impl_actions!(
vim,
[FindCommand, ReplaceCommand, Search, MoveToPrev, MoveToNext]
);
impl_actions!(vim, [FindCommand, Search, MoveToPrev, MoveToNext]);
impl_internal_actions!(vim, [ReplaceCommand]);
pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, Vim::move_to_next);

View file

@ -10,15 +10,14 @@ use editor::{
movement::{self, FindRange},
Bias, DisplayPoint, Editor,
};
use itertools::Itertools;
use gpui::{actions, impl_actions, ViewContext};
use itertools::Itertools;
use language::{BufferSnapshot, CharKind, Point, Selection, TextObject, TreeSitterOptions};
use multi_buffer::MultiBufferRow;
use schemars::JsonSchema;
use serde::Deserialize;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema)]
pub enum Object {
Word { ignore_punctuation: bool },
Sentence,
@ -40,13 +39,14 @@ pub enum Object {
Comment,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
struct Word {
#[serde(default)]
ignore_punctuation: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
#[serde(rename_all = "camelCase")]
struct IndentObj {
#[serde(default)]

View file

@ -1,6 +1,3 @@
use std::borrow::BorrowMut;
use std::{fmt::Display, ops::Range, sync::Arc};
use crate::command::command_interceptor;
use crate::normal::repeat::Replayer;
use crate::surrounds::SurroundsType;
@ -13,12 +10,15 @@ use gpui::{
Action, AppContext, BorrowAppContext, ClipboardEntry, ClipboardItem, Global, View, WeakView,
};
use language::Point;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
use std::borrow::BorrowMut;
use std::{fmt::Display, ops::Range, sync::Arc};
use ui::{SharedString, ViewContext};
use workspace::searchable::Direction;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)]
pub enum Mode {
Normal,
Insert,
@ -59,22 +59,39 @@ impl Default for Mode {
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema)]
pub enum Operator {
Change,
Delete,
Yank,
Replace,
Object { around: bool },
FindForward { before: bool },
FindBackward { after: bool },
Sneak { first_char: Option<char> },
SneakBackward { first_char: Option<char> },
AddSurrounds { target: Option<SurroundsType> },
ChangeSurrounds { target: Option<Object> },
Object {
around: bool,
},
FindForward {
before: bool,
},
FindBackward {
after: bool,
},
Sneak {
first_char: Option<char>,
},
SneakBackward {
first_char: Option<char>,
},
AddSurrounds {
#[serde(skip)]
target: Option<SurroundsType>,
},
ChangeSurrounds {
target: Option<Object>,
},
DeleteSurrounds,
Mark,
Jump { line: bool },
Jump {
line: bool,
},
Indent,
Outdent,
AutoIndent,
@ -82,8 +99,12 @@ pub enum Operator {
Lowercase,
Uppercase,
OppositeCase,
Digraph { first_char: Option<char> },
Literal { prefix: Option<String> },
Digraph {
first_char: Option<char>,
},
Literal {
prefix: Option<String>,
},
Register,
RecordRegister,
ReplayRegister,

View file

@ -6,7 +6,7 @@ use crate::{
};
use editor::{movement, scroll::Autoscroll, Bias};
use language::BracketPair;
use serde::Deserialize;
use std::sync::Arc;
use ui::ViewContext;
@ -17,16 +17,6 @@ pub enum SurroundsType {
Selection,
}
// This exists so that we can have Deserialize on Operators, but not on Motions.
impl<'de> Deserialize<'de> for SurroundsType {
fn deserialize<D>(_: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Err(serde::de::Error::custom("Cannot deserialize SurroundsType"))
}
}
impl Vim {
pub fn add_surrounds(
&mut self,

View file

@ -49,25 +49,25 @@ use workspace::{self, Pane, ResizeIntent, Workspace};
use crate::state::ReplayableAction;
/// Used to resize the current pane
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
pub struct ResizePane(pub ResizeIntent);
/// An Action to Switch between modes
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
pub struct SwitchMode(pub Mode);
/// PushOperator is used to put vim into a "minor" mode,
/// where it's waiting for a specific next set of keystrokes.
/// For example 'd' needs a motion to complete.
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
pub struct PushOperator(pub Operator);
/// Number is used to manage vim's count. Pushing a digit
/// multiplis the current value by 10 and adds the digit.
#[derive(Clone, Deserialize, PartialEq)]
/// multiplies the current value by 10 and adds the digit.
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
struct Number(usize);
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
struct SelectRegister(String);
actions!(

View file

@ -25,6 +25,7 @@ use itertools::Itertools;
use language::DiagnosticSeverity;
use parking_lot::Mutex;
use project::{Project, ProjectEntryId, ProjectPath, WorktreeId};
use schemars::JsonSchema;
use serde::Deserialize;
use settings::{Settings, SettingsStore};
use std::{
@ -71,7 +72,7 @@ impl DraggedSelection {
}
}
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
#[derive(Clone, Copy, PartialEq, Debug, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum SaveIntent {
/// write all files (even if unchanged)
@ -92,16 +93,16 @@ pub enum SaveIntent {
Skip,
}
#[derive(Clone, Deserialize, PartialEq, Debug)]
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
pub struct ActivateItem(pub usize);
#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
pub struct CloseActiveItem {
pub save_intent: Option<SaveIntent>,
}
#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
pub struct CloseInactiveItems {
pub save_intent: Option<SaveIntent>,
@ -109,7 +110,7 @@ pub struct CloseInactiveItems {
pub close_pinned: bool,
}
#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
pub struct CloseAllItems {
pub save_intent: Option<SaveIntent>,
@ -117,34 +118,35 @@ pub struct CloseAllItems {
pub close_pinned: bool,
}
#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
pub struct CloseCleanItems {
#[serde(default)]
pub close_pinned: bool,
}
#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
pub struct CloseItemsToTheRight {
#[serde(default)]
pub close_pinned: bool,
}
#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
pub struct CloseItemsToTheLeft {
#[serde(default)]
pub close_pinned: bool,
}
#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase")]
pub struct RevealInProjectPanel {
#[serde(skip)]
pub entry_id: Option<u64>,
}
#[derive(Default, PartialEq, Clone, Deserialize)]
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]
pub struct DeploySearch {
#[serde(default)]
pub replace_enabled: bool,

View file

@ -13,6 +13,7 @@ use gpui::{
};
use parking_lot::Mutex;
use project::Project;
use schemars::JsonSchema;
use serde::Deserialize;
use settings::Settings;
use std::sync::Arc;
@ -717,7 +718,7 @@ impl PaneAxis {
}
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, JsonSchema)]
pub enum SplitDirection {
Up,
Down,
@ -800,7 +801,7 @@ impl SplitDirection {
}
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, PartialEq)]
pub enum ResizeIntent {
Lengthen,
Shorten,

View file

@ -61,10 +61,9 @@ use persistence::{
SerializedWindowBounds, DB,
};
use postage::stream::Stream;
use project::{
DirectoryLister, Project, ProjectEntryId, ProjectPath, ResolvedPath, Worktree, WorktreeId,
};
use project::{DirectoryLister, Project, ProjectEntryId, ProjectPath, ResolvedPath, Worktree};
use remote::{ssh_session::ConnectionIdentifier, SshClientDelegate, SshConnectionOptions};
use schemars::JsonSchema;
use serde::Deserialize;
use session::AppSession;
use settings::Settings;
@ -119,9 +118,6 @@ static ZED_WINDOW_POSITION: LazyLock<Option<Point<Pixels>>> = LazyLock::new(|| {
.and_then(parse_pixel_position_env_var)
});
#[derive(Clone, PartialEq)]
pub struct RemoveWorktreeFromProject(pub WorktreeId);
actions!(assistant, [ShowConfiguration]);
actions!(
@ -165,64 +161,64 @@ pub struct OpenPaths {
pub paths: Vec<PathBuf>,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, PartialEq, JsonSchema)]
pub struct ActivatePane(pub usize);
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, PartialEq, JsonSchema)]
pub struct ActivatePaneInDirection(pub SplitDirection);
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, PartialEq, JsonSchema)]
pub struct SwapPaneInDirection(pub SplitDirection);
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, PartialEq, JsonSchema)]
pub struct MoveItemToPane {
pub destination: usize,
#[serde(default = "default_true")]
pub focus: bool,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, PartialEq, JsonSchema)]
pub struct MoveItemToPaneInDirection {
pub direction: SplitDirection,
#[serde(default = "default_true")]
pub focus: bool,
}
#[derive(Clone, PartialEq, Debug, Deserialize)]
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct SaveAll {
pub save_intent: Option<SaveIntent>,
}
#[derive(Clone, PartialEq, Debug, Deserialize)]
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct Save {
pub save_intent: Option<SaveIntent>,
}
#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
#[derive(Clone, PartialEq, Debug, Deserialize, Default, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct CloseAllItemsAndPanes {
pub save_intent: Option<SaveIntent>,
}
#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
#[derive(Clone, PartialEq, Debug, Deserialize, Default, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct CloseInactiveTabsAndPanes {
pub save_intent: Option<SaveIntent>,
}
#[derive(Clone, Deserialize, PartialEq)]
#[derive(Clone, Deserialize, PartialEq, JsonSchema)]
pub struct SendKeystrokes(pub String);
#[derive(Clone, Deserialize, PartialEq, Default)]
#[derive(Clone, Deserialize, PartialEq, Default, JsonSchema)]
pub struct Reload {
pub binary_path: Option<PathBuf>,
}
action_as!(project_symbols, ToggleProjectSymbols as Toggle);
#[derive(Default, PartialEq, Eq, Clone, serde::Deserialize)]
#[derive(Default, PartialEq, Eq, Clone, Deserialize, JsonSchema)]
pub struct ToggleFileFinder {
#[serde(default)]
pub separate_history: bool,
@ -299,7 +295,7 @@ impl PartialEq for Toast {
}
}
#[derive(Debug, Default, Clone, Deserialize, PartialEq)]
#[derive(Debug, Default, Clone, Deserialize, PartialEq, JsonSchema)]
pub struct OpenTerminal {
pub working_directory: PathBuf,
}

View file

@ -11,12 +11,12 @@ use serde::{Deserialize, Serialize};
// https://github.com/mmastrac/rust-ctor/issues/280
pub fn init() {}
#[derive(Clone, PartialEq, Deserialize)]
#[derive(Clone, PartialEq, Deserialize, JsonSchema)]
pub struct OpenBrowser {
pub url: String,
}
#[derive(Clone, PartialEq, Deserialize)]
#[derive(Clone, PartialEq, Deserialize, JsonSchema)]
pub struct OpenZedUrl {
pub url: String,
}
@ -65,9 +65,10 @@ pub mod feedback {
pub mod theme_selector {
use gpui::impl_actions;
use schemars::JsonSchema;
use serde::Deserialize;
#[derive(PartialEq, Clone, Default, Debug, Deserialize)]
#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema)]
pub struct Toggle {
/// A list of theme names to filter the theme selector down to.
pub themes_filter: Option<Vec<String>>,
@ -76,20 +77,21 @@ pub mod theme_selector {
impl_actions!(theme_selector, [Toggle]);
}
#[derive(Clone, Default, Deserialize, PartialEq)]
#[derive(Clone, Default, Deserialize, PartialEq, JsonSchema)]
pub struct InlineAssist {
pub prompt: Option<String>,
}
impl_actions!(assistant, [InlineAssist]);
#[derive(PartialEq, Clone, Deserialize, Default)]
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct OpenRecent {
#[serde(default)]
pub create_new_window: bool,
}
gpui::impl_actions!(projects, [OpenRecent]);
gpui::actions!(projects, [OpenRemote]);
impl_actions!(projects, [OpenRecent]);
actions!(projects, [OpenRemote]);
/// Where to spawn the task in the UI.
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
@ -102,8 +104,8 @@ pub enum RevealTarget {
Dock,
}
/// Spawn a task with name or open tasks modal
#[derive(Debug, PartialEq, Clone, Deserialize)]
/// Spawn a task with name or open tasks modal.
#[derive(Debug, PartialEq, Clone, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum Spawn {
/// Spawns a task by the name given.
@ -128,8 +130,8 @@ impl Spawn {
}
}
/// Rerun last task
#[derive(PartialEq, Clone, Deserialize, Default)]
/// Rerun the last task.
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
pub struct Rerun {
/// Controls whether the task context is reevaluated prior to execution of a task.
/// If it is not, environment variables such as ZED_COLUMN, ZED_FILE are gonna be the same as in the last execution of a task
@ -147,6 +149,7 @@ pub struct Rerun {
pub use_new_terminal: Option<bool>,
/// If present, rerun the task with this ID, otherwise rerun the last task.
#[serde(skip)]
pub task_id: Option<String>,
}