Detailed changes
@@ -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",
@@ -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;
@@ -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,
@@ -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 {
}
}
+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 type MacroActionBuilder = fn() -> 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 ActionData {
+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,
+ ))
}
);
@@ -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()
}
@@ -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;
@@ -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]);
@@ -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)]
@@ -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>>,
}
@@ -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
@@ -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)]
@@ -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
@@ -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,
}
@@ -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,
@@ -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
@@ -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,
@@ -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))),
+ fn set<I, O>(input: I) -> Option<O>
+ where
+ I: Into<O>,
+ {
+ Some(input.into())
+ }
+
+ 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()
- }),
- ];
- 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(),
- )]),
+ };
+ 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());
+ }
+ }
}
- let action_schema = Schema::Object(SchemaObject {
- subschemas: Some(Box::new(SubschemaValidation {
- one_of: Some(alternatives),
+
+ // 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);
@@ -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
@@ -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,
@@ -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
@@ -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]);
@@ -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
@@ -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)]
@@ -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,
@@ -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]);
@@ -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)]
@@ -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)]
@@ -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)]
@@ -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);
@@ -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)]
@@ -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,
@@ -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,
@@ -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!(
@@ -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,
@@ -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,
@@ -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,
}
@@ -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>,
}