Restore impl_actions! and remove derive(Action)

Conrad Irwin created

Change summary

crates/collab_ui2/src/collab_panel.rs          | 16 +-
crates/command_palette2/src/command_palette.rs |  2 
crates/editor2/src/editor.rs                   | 49 +++++++---
crates/gpui2/src/action.rs                     | 58 ++++++-----
crates/gpui2/tests/action_macros.rs            | 13 ++
crates/gpui2_macros/src/action.rs              | 97 --------------------
crates/gpui2_macros/src/gpui2_macros.rs        | 12 -
crates/gpui2_macros/src/register_action.rs     | 42 -------
crates/live_kit_client2/examples/test_app2.rs  |  6 
crates/search2/src/buffer_search.rs            |  6 
crates/terminal_view2/src/terminal_view.rs     | 14 +-
crates/workspace2/src/pane.rs                  | 18 +-
crates/workspace2/src/workspace2.rs            | 42 +++++--
crates/zed_actions2/src/lib.rs                 |  8 +
14 files changed, 147 insertions(+), 236 deletions(-)

Detailed changes

crates/collab_ui2/src/collab_panel.rs 🔗

@@ -92,7 +92,7 @@ use theme::{ActiveTheme, ThemeSettings};
 //     channel_id: ChannelId,
 // }
 
-#[derive(Action, PartialEq, Debug, Clone, Serialize, Deserialize)]
+#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
 pub struct OpenChannelNotes {
     pub channel_id: ChannelId,
 }
@@ -122,6 +122,8 @@ pub struct OpenChannelNotes {
 //     to: ChannelId,
 // }
 
+impl_actions!(collab_panel, [OpenChannelNotes]);
+
 actions!(
     collab_panel,
     [
@@ -172,12 +174,12 @@ use editor::Editor;
 use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt};
 use fuzzy::{match_strings, StringMatchCandidate};
 use gpui::{
-    actions, canvas, div, img, overlay, point, prelude::*, px, rems, serde_json, size, Action,
-    AppContext, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent, Div, EventEmitter,
-    FocusHandle, Focusable, FocusableView, Hsla, InteractiveElement, IntoElement, Length, Model,
-    MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Quad, Render, RenderOnce,
-    ScrollHandle, SharedString, Size, Stateful, Styled, Subscription, Task, View, ViewContext,
-    VisualContext, WeakView,
+    actions, canvas, div, img, impl_actions, overlay, point, prelude::*, px, rems, serde_json,
+    size, Action, AppContext, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent, Div,
+    EventEmitter, FocusHandle, Focusable, FocusableView, Hsla, InteractiveElement, IntoElement,
+    Length, Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Quad, Render,
+    RenderOnce, ScrollHandle, SharedString, Size, Stateful, Styled, Subscription, Task, View,
+    ViewContext, VisualContext, WeakView,
 };
 use project::{Fs, Project};
 use serde_derive::{Deserialize, Serialize};

crates/command_palette2/src/command_palette.rs 🔗

@@ -49,7 +49,7 @@ impl CommandPalette {
             .available_actions()
             .into_iter()
             .filter_map(|action| {
-                let name = gpui::remove_the_2(action.name());
+                let name = action.name();
                 let namespace = name.split("::").next().unwrap_or("malformed action name");
                 if filter.is_some_and(|f| {
                     f.hidden_namespaces.contains(namespace)

crates/editor2/src/editor.rs 🔗

@@ -39,8 +39,8 @@ use futures::FutureExt;
 use fuzzy::{StringMatch, StringMatchCandidate};
 use git::diff_hunk_to_display;
 use gpui::{
-    actions, div, point, prelude::*, px, relative, rems, size, uniform_list, Action, AnyElement,
-    AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Context,
+    actions, div, impl_actions, point, prelude::*, px, relative, rems, size, uniform_list, Action,
+    AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Context,
     DispatchPhase, Div, ElementId, EventEmitter, FocusHandle, FocusableView, FontFeatures,
     FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, InteractiveText, KeyContext, Model,
     MouseButton, ParentElement, Pixels, Render, RenderOnce, SharedString, Styled, StyledText,
@@ -185,82 +185,101 @@ pub fn render_parsed_markdown(
     })
 }
 
-#[derive(PartialEq, Clone, Deserialize, Default, Action)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct SelectNext {
     #[serde(default)]
     pub replace_newest: bool,
 }
 
-#[derive(PartialEq, Clone, Deserialize, Default, Action)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct SelectPrevious {
     #[serde(default)]
     pub replace_newest: bool,
 }
 
-#[derive(PartialEq, Clone, Deserialize, Default, Action)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct SelectAllMatches {
     #[serde(default)]
     pub replace_newest: bool,
 }
 
-#[derive(PartialEq, Clone, Deserialize, Default, Action)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct SelectToBeginningOfLine {
     #[serde(default)]
     stop_at_soft_wraps: bool,
 }
 
-#[derive(PartialEq, Clone, Deserialize, Default, Action)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct MovePageUp {
     #[serde(default)]
     center_cursor: bool,
 }
 
-#[derive(PartialEq, Clone, Deserialize, Default, Action)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct MovePageDown {
     #[serde(default)]
     center_cursor: bool,
 }
 
-#[derive(PartialEq, Clone, Deserialize, Default, Action)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct SelectToEndOfLine {
     #[serde(default)]
     stop_at_soft_wraps: bool,
 }
 
-#[derive(PartialEq, Clone, Deserialize, Default, Action)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct ToggleCodeActions {
     #[serde(default)]
     pub deployed_from_indicator: bool,
 }
 
-#[derive(PartialEq, Clone, Deserialize, Default, Action)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct ConfirmCompletion {
     #[serde(default)]
     pub item_ix: Option<usize>,
 }
 
-#[derive(PartialEq, Clone, Deserialize, Default, Action)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct ConfirmCodeAction {
     #[serde(default)]
     pub item_ix: Option<usize>,
 }
 
-#[derive(PartialEq, Clone, Deserialize, Default, Action)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct ToggleComments {
     #[serde(default)]
     pub advance_downwards: bool,
 }
 
-#[derive(PartialEq, Clone, Deserialize, Default, Action)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct FoldAt {
     pub buffer_row: u32,
 }
 
-#[derive(PartialEq, Clone, Deserialize, Default, Action)]
+#[derive(PartialEq, Clone, Deserialize, Default)]
 pub struct UnfoldAt {
     pub buffer_row: u32,
 }
 
+impl_actions!(
+    editor,
+    [
+        SelectNext,
+        SelectPrevious,
+        SelectAllMatches,
+        SelectToBeginningOfLine,
+        MovePageUp,
+        MovePageDown,
+        SelectToEndOfLine,
+        ToggleCodeActions,
+        ConfirmCompletion,
+        ConfirmCodeAction,
+        ToggleComments,
+        FoldAt,
+        UnfoldAt
+    ]
+);
+
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub enum InlayId {
     Suggestion(usize),

crates/gpui2/src/action.rs 🔗

@@ -3,33 +3,32 @@ use anyhow::{anyhow, Context, Result};
 use collections::HashMap;
 pub use no_action::NoAction;
 use serde_json::json;
-use std::{
-    any::{Any, TypeId},
-    ops::Deref,
-};
+use std::any::{Any, TypeId};
 
 /// Actions are used to implement keyboard-driven UI.
 /// When you declare an action, you can bind keys to the action in the keymap and
 /// listeners for that action in the element tree.
 ///
 /// To declare a list of simple actions, you can use the actions! macro, which defines a simple unit struct
-/// action for each listed action name.
+/// action for each listed action name in the given namespace.
 /// ```rust
-/// actions!(MoveUp, MoveDown, MoveLeft, MoveRight, Newline);
+/// actions!(editor, [MoveUp, MoveDown, MoveLeft, MoveRight, Newline]);
 /// ```
-/// More complex data types can also be actions. If you annotate your type with the action derive macro
-/// it will be implemented and registered automatically.
+/// More complex data types can also be actions, providing they implement Clone, PartialEq, Debug,
+/// and serde_derive::Deserialize.
+/// Use `impl_actions!` to automatically implement the action in the given namespace.
 /// ```
-/// #[derive(Clone, PartialEq, serde_derive::Deserialize, Action)]
+/// #[derive(Clone, PartialEq, serde_derive::Deserialize, Debug)]
 /// pub struct SelectNext {
 ///     pub replace_newest: bool,
 /// }
+/// impl_actions!(editor, [SelectNext]);
+/// ```
 ///
 /// If you want to control the behavior of the action trait manually, you can use the lower-level `#[register_action]`
 /// macro, which only generates the code needed to register your action before `main`.
 ///
 /// ```
-/// #[gpui::register_action]
 /// #[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::fmt::Debug)]
 /// pub struct Paste {
 ///     pub content: SharedString,
@@ -38,6 +37,7 @@ use std::{
 /// impl gpui::Action for Paste {
 ///      ///...
 /// }
+/// register_action!(Paste);
 /// ```
 pub trait Action: 'static {
     fn boxed_clone(&self) -> Box<dyn Action>;
@@ -56,7 +56,7 @@ pub trait Action: 'static {
 impl std::fmt::Debug for dyn Action {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_struct("dyn Action")
-            .field("type_name", &self.name())
+            .field("name", &self.name())
             .finish()
     }
 }
@@ -115,7 +115,7 @@ impl ActionRegistry {
         for builder in __GPUI_ACTIONS {
             let action = builder();
             //todo(remove)
-            let name: SharedString = remove_the_2(action.name).into();
+            let name: SharedString = action.name.into();
             self.builders_by_name.insert(name.clone(), action.build);
             self.names_by_type_id.insert(action.type_id, name.clone());
             self.all_names.push(name);
@@ -139,11 +139,9 @@ impl ActionRegistry {
         name: &str,
         params: Option<serde_json::Value>,
     ) -> Result<Box<dyn Action>> {
-        //todo(remove)
-        let name = remove_the_2(name);
         let build_action = self
             .builders_by_name
-            .get(name.deref())
+            .get(name)
             .ok_or_else(|| anyhow!("no action type registered for {}", name))?;
         (build_action)(params.unwrap_or_else(|| json!({})))
             .with_context(|| format!("Attempting to build action {}", name))
@@ -155,14 +153,13 @@ impl ActionRegistry {
 }
 
 /// Defines unit structs that can be used as actions.
-/// To use more complex data types as actions, annotate your type with the #[action] macro.
+/// To use more complex data types as actions, use `impl_actions!`
 #[macro_export]
 macro_rules! actions {
     ($namespace:path, [ $($name:ident),* $(,)? ]) => {
         $(
             #[derive(::std::cmp::PartialEq, ::std::clone::Clone, ::std::default::Default, gpui::serde_derive::Deserialize)]
             #[serde(crate = "gpui::serde")]
-            #[gpui::register_action]
             pub struct $name;
 
             gpui::__impl_action!($namespace, $name,
@@ -170,6 +167,22 @@ macro_rules! actions {
                     Ok(Box::new(Self))
                 }
             );
+
+            gpui::register_action!($name);
+        )*
+    };
+}
+
+/// Implements the Action trait for any struct that implements Clone, Default, PartialEq, and serde_deserialize::Deserialize
+#[macro_export]
+macro_rules! impl_actions {
+    ($namespace:path, [ $($name:ident),* $(,)? ]) => {
+        $(
+            gpui::__impl_action!($namespace, $name,
+                fn build(value: gpui::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>> {
+                    Ok(std::boxed::Box::new(gpui::serde_json::from_value::<Self>(value)?))
+                }
+            );
         )*
     };
 }
@@ -220,17 +233,6 @@ macro_rules! __impl_action {
     };
 }
 
-//todo!(remove)
-pub fn remove_the_2(action_name: &str) -> String {
-    let mut separator_matches = action_name.rmatch_indices("::");
-    separator_matches.next().unwrap();
-    let name_start_ix = separator_matches.next().map_or(0, |(ix, _)| ix + 2);
-    // todo!() remove the 2 replacement when migration is done
-    action_name[name_start_ix..]
-        .replace("2::", "::")
-        .to_string()
-}
-
 mod no_action {
     use crate as gpui;
 

crates/gpui2/tests/action_macros.rs 🔗

@@ -1,16 +1,23 @@
+use gpui2::{actions, impl_actions};
+use gpui2_macros::register_action;
 use serde_derive::Deserialize;
 
 #[test]
-fn test_derive() {
+fn test_action_macros() {
     use gpui2 as gpui;
 
-    #[derive(PartialEq, Clone, Deserialize, gpui2_macros::Action)]
+    actions!(test, [TestAction]);
+
+    #[derive(PartialEq, Clone, Deserialize)]
     struct AnotherTestAction;
 
-    #[gpui2_macros::register_action]
+    impl_actions!(test, [AnotherTestAction]);
+
     #[derive(PartialEq, Clone, gpui::serde_derive::Deserialize)]
     struct RegisterableAction {}
 
+    register_action!(RegisterableAction);
+
     impl gpui::Action for RegisterableAction {
         fn boxed_clone(&self) -> Box<dyn gpui::Action> {
             todo!()

crates/gpui2_macros/src/action.rs 🔗

@@ -1,97 +0,0 @@
-// Input:
-//
-// #[action]
-// struct Foo {
-//   bar: String,
-// }
-
-// Output:
-//
-// #[gpui::register_action]
-// #[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::default::Default, std::fmt::Debug)]
-// struct Foo {
-//   bar: String,
-// }
-
-use proc_macro::TokenStream;
-use quote::quote;
-use syn::{parse_macro_input, DeriveInput, Error};
-
-use crate::register_action::register_action;
-
-pub fn action(input: TokenStream) -> TokenStream {
-    let input = parse_macro_input!(input as DeriveInput);
-
-    let name = &input.ident;
-
-    if input.generics.lt_token.is_some() {
-        return Error::new(name.span(), "Actions must be a concrete type")
-            .into_compile_error()
-            .into();
-    }
-
-    let is_unit_struct = match input.data {
-        syn::Data::Struct(struct_data) => struct_data.fields.is_empty(),
-        syn::Data::Enum(_) => false,
-        syn::Data::Union(_) => false,
-    };
-
-    let build_impl = if is_unit_struct {
-        quote! {
-            let _ = value;
-            Ok(std::boxed::Box::new(Self {}))
-        }
-    } else {
-        quote! {
-            Ok(std::boxed::Box::new(gpui::serde_json::from_value::<Self>(value)?))
-        }
-    };
-
-    let register_action = register_action(&name);
-
-    let output = quote! {
-        const _: fn() = || {
-            fn assert_impl<T: ?Sized + for<'a> gpui::serde::Deserialize<'a> +  ::std::cmp::PartialEq + ::std::clone::Clone>() {}
-            assert_impl::<#name>();
-        };
-
-        impl gpui::Action for #name {
-            fn name(&self) -> &'static str
-            {
-                ::std::any::type_name::<#name>()
-            }
-
-            fn debug_name() -> &'static str
-            where
-                Self: ::std::marker::Sized
-            {
-                ::std::any::type_name::<#name>()
-            }
-
-            fn build(value: gpui::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>>
-            where
-                Self: ::std::marker::Sized {
-                    #build_impl
-            }
-
-            fn partial_eq(&self, action: &dyn gpui::Action) -> bool {
-                action
-                    .as_any()
-                    .downcast_ref::<Self>()
-                    .map_or(false, |a| self == a)
-            }
-
-            fn boxed_clone(&self) ->  std::boxed::Box<dyn gpui::Action> {
-                ::std::boxed::Box::new(self.clone())
-            }
-
-            fn as_any(&self) -> &dyn ::std::any::Any {
-                self
-            }
-        }
-
-        #register_action
-    };
-
-    TokenStream::from(output)
-}

crates/gpui2_macros/src/gpui2_macros.rs 🔗

@@ -1,4 +1,3 @@
-mod action;
 mod derive_into_element;
 mod register_action;
 mod style_helpers;
@@ -6,14 +5,9 @@ mod test;
 
 use proc_macro::TokenStream;
 
-#[proc_macro_derive(Action)]
-pub fn action(input: TokenStream) -> TokenStream {
-    action::action(input)
-}
-
-#[proc_macro_attribute]
-pub fn register_action(attr: TokenStream, item: TokenStream) -> TokenStream {
-    register_action::register_action_macro(attr, item)
+#[proc_macro]
+pub fn register_action(ident: TokenStream) -> TokenStream {
+    register_action::register_action_macro(ident)
 }
 
 #[proc_macro_derive(IntoElement)]

crates/gpui2_macros/src/register_action.rs 🔗

@@ -14,47 +14,13 @@
 use proc_macro::TokenStream;
 use proc_macro2::Ident;
 use quote::{format_ident, quote};
-use syn::{parse_macro_input, DeriveInput, Error};
+use syn::parse_macro_input;
 
-pub fn register_action_macro(_attr: TokenStream, item: TokenStream) -> TokenStream {
-    let input = parse_macro_input!(item as DeriveInput);
-    let registration = register_action(&input.ident);
-
-    let has_action_derive = input
-        .attrs
-        .iter()
-        .find(|attr| {
-            (|| {
-                let meta = attr.parse_meta().ok()?;
-                meta.path().is_ident("derive").then(|| match meta {
-                    syn::Meta::Path(_) => None,
-                    syn::Meta::NameValue(_) => None,
-                    syn::Meta::List(list) => list
-                        .nested
-                        .iter()
-                        .find(|list| match list {
-                            syn::NestedMeta::Meta(meta) => meta.path().is_ident("Action"),
-                            syn::NestedMeta::Lit(_) => false,
-                        })
-                        .map(|_| true),
-                })?
-            })()
-            .unwrap_or(false)
-        })
-        .is_some();
-
-    if has_action_derive {
-        return Error::new(
-            input.ident.span(),
-            "The Action derive macro has already registered this action",
-        )
-        .into_compile_error()
-        .into();
-    }
+pub fn register_action_macro(ident: TokenStream) -> TokenStream {
+    let name = parse_macro_input!(ident as Ident);
+    let registration = register_action(&name);
 
     TokenStream::from(quote! {
-        #input
-
         #registration
     })
 }

crates/live_kit_client2/examples/test_app2.rs 🔗

@@ -1,17 +1,15 @@
 use std::{sync::Arc, time::Duration};
 
 use futures::StreamExt;
-use gpui::{Action, KeyBinding};
+use gpui::{actions, KeyBinding};
 use live_kit_client2::{
     LocalAudioTrack, LocalVideoTrack, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate, Room,
 };
 use live_kit_server::token::{self, VideoGrant};
 use log::LevelFilter;
-use serde_derive::Deserialize;
 use simplelog::SimpleLogger;
 
-#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Action)]
-struct Quit;
+actions!(live_kit_client, [Quit]);
 
 fn main() {
     SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");

crates/search2/src/buffer_search.rs 🔗

@@ -10,7 +10,7 @@ use collections::HashMap;
 use editor::{Editor, EditorMode};
 use futures::channel::oneshot;
 use gpui::{
-    actions, div, red, Action, AppContext, Div, EventEmitter, FocusableView,
+    actions, div, impl_actions, red, Action, AppContext, Div, EventEmitter, FocusableView,
     InteractiveElement as _, IntoElement, KeyContext, ParentElement as _, Render, Styled,
     Subscription, Task, View, ViewContext, VisualContext as _, WeakView, WindowContext,
 };
@@ -26,11 +26,13 @@ use workspace::{
     ToolbarItemLocation, ToolbarItemView,
 };
 
-#[derive(PartialEq, Clone, Deserialize, Default, Action)]
+#[derive(PartialEq, Clone, Deserialize)]
 pub struct Deploy {
     pub focus: bool,
 }
 
+impl_actions!(buffer_search, [Deploy]);
+
 actions!(buffer_search, [Dismiss, FocusEditor]);
 
 pub enum Event {

crates/terminal_view2/src/terminal_view.rs 🔗

@@ -9,10 +9,10 @@ pub mod terminal_panel;
 // use crate::terminal_element::TerminalElement;
 use editor::{scroll::autoscroll::Autoscroll, Editor};
 use gpui::{
-    div, overlay, Action, AnyElement, AppContext, DismissEvent, Div, EventEmitter, FocusEvent,
-    FocusHandle, Focusable, FocusableElement, FocusableView, KeyContext, KeyDownEvent, Keystroke,
-    Model, MouseButton, MouseDownEvent, Pixels, Render, Styled, Subscription, Task, View,
-    VisualContext, WeakView,
+    div, impl_actions, overlay, AnyElement, AppContext, DismissEvent, Div, EventEmitter,
+    FocusEvent, FocusHandle, Focusable, FocusableElement, FocusableView, KeyContext, KeyDownEvent,
+    Keystroke, Model, MouseButton, MouseDownEvent, Pixels, Render, Styled, Subscription, Task,
+    View, VisualContext, WeakView,
 };
 use language::Bias;
 use persistence::TERMINAL_DB;
@@ -55,12 +55,14 @@ const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
 #[derive(Clone, Debug, PartialEq)]
 pub struct ScrollTerminal(pub i32);
 
-#[derive(Clone, Debug, Default, Deserialize, PartialEq, Action)]
+#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
 pub struct SendText(String);
 
-#[derive(Clone, Debug, Default, Deserialize, PartialEq, Action)]
+#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
 pub struct SendKeystroke(String);
 
+impl_actions!(terminal_view, [SendText, SendKeystroke]);
+
 pub fn init(cx: &mut AppContext) {
     terminal_panel::init(cx);
     terminal::init(cx);

crates/workspace2/src/pane.rs 🔗

@@ -7,10 +7,10 @@ use crate::{
 use anyhow::Result;
 use collections::{HashMap, HashSet, VecDeque};
 use gpui::{
-    actions, overlay, prelude::*, rems, Action, AnchorCorner, AnyWeakView, AppContext,
-    AsyncWindowContext, DismissEvent, Div, EntityId, EventEmitter, FocusHandle, Focusable,
-    FocusableView, Model, MouseButton, NavigationDirection, Pixels, Point, PromptLevel, Render,
-    Task, View, ViewContext, VisualContext, WeakView, WindowContext,
+    actions, impl_actions, overlay, prelude::*, rems, Action, AnchorCorner, AnyWeakView,
+    AppContext, AsyncWindowContext, DismissEvent, Div, EntityId, EventEmitter, FocusHandle,
+    Focusable, FocusableView, Model, MouseButton, NavigationDirection, Pixels, Point, PromptLevel,
+    Render, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
 };
 use parking_lot::Mutex;
 use project::{Project, ProjectEntryId, ProjectPath};
@@ -52,9 +52,7 @@ pub enum SaveIntent {
     Skip,
 }
 
-//todo!("Do we need the default bound on actions? Decide soon")
-// #[register_action]
-#[derive(Action, Clone, Deserialize, PartialEq, Debug)]
+#[derive(Clone, Deserialize, PartialEq, Debug)]
 pub struct ActivateItem(pub usize);
 
 // #[derive(Clone, PartialEq)]
@@ -75,18 +73,20 @@ pub struct ActivateItem(pub usize);
 //     pub pane: WeakView<Pane>,
 // }
 
-#[derive(Clone, PartialEq, Debug, Deserialize, Default, Action)]
+#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
 #[serde(rename_all = "camelCase")]
 pub struct CloseActiveItem {
     pub save_intent: Option<SaveIntent>,
 }
 
-#[derive(Clone, PartialEq, Debug, Deserialize, Default, Action)]
+#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
 #[serde(rename_all = "camelCase")]
 pub struct CloseAllItems {
     pub save_intent: Option<SaveIntent>,
 }
 
+impl_actions!(pane, [CloseAllItems, CloseActiveItem, ActivateItem]);
+
 actions!(
     pane,
     [

crates/workspace2/src/workspace2.rs 🔗

@@ -29,12 +29,12 @@ use futures::{
     Future, FutureExt, StreamExt,
 };
 use gpui::{
-    actions, div, point, size, Action, AnyModel, AnyView, AnyWeakView, AnyWindowHandle, AppContext,
-    AsyncAppContext, AsyncWindowContext, Bounds, Context, Div, Entity, EntityId, EventEmitter,
-    FocusHandle, FocusableView, GlobalPixels, InteractiveElement, KeyContext, ManagedView, Model,
-    ModelContext, ParentElement, PathPromptOptions, Point, PromptLevel, Render, Size, Styled,
-    Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowBounds, WindowContext,
-    WindowHandle, WindowOptions,
+    actions, div, impl_actions, point, size, Action, AnyModel, AnyView, AnyWeakView,
+    AnyWindowHandle, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div, Entity,
+    EntityId, EventEmitter, FocusHandle, FocusableView, GlobalPixels, InteractiveElement,
+    KeyContext, ManagedView, Model, ModelContext, ParentElement, PathPromptOptions, Point,
+    PromptLevel, Render, Size, Styled, Subscription, Task, View, ViewContext, VisualContext,
+    WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
 };
 use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
 use itertools::Itertools;
@@ -125,36 +125,50 @@ pub struct OpenPaths {
     pub paths: Vec<PathBuf>,
 }
 
-#[derive(Clone, Deserialize, PartialEq, Action)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct ActivatePane(pub usize);
 
-#[derive(Clone, Deserialize, PartialEq, Action)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct ActivatePaneInDirection(pub SplitDirection);
 
-#[derive(Clone, Deserialize, PartialEq, Action)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct SwapPaneInDirection(pub SplitDirection);
 
-#[derive(Clone, Deserialize, PartialEq, Action)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct NewFileInDirection(pub SplitDirection);
 
-#[derive(Clone, PartialEq, Debug, Deserialize, Action)]
+#[derive(Clone, PartialEq, Debug, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct SaveAll {
     pub save_intent: Option<SaveIntent>,
 }
 
-#[derive(Clone, PartialEq, Debug, Deserialize, Action)]
+#[derive(Clone, PartialEq, Debug, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Save {
     pub save_intent: Option<SaveIntent>,
 }
 
-#[derive(Clone, PartialEq, Debug, Deserialize, Default, Action)]
+#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
 #[serde(rename_all = "camelCase")]
 pub struct CloseAllItemsAndPanes {
     pub save_intent: Option<SaveIntent>,
 }
 
+impl_actions!(
+    workspace,
+    [
+        ActivatePane,
+        ActivatePaneInDirection,
+        CloseAllItemsAndPanes,
+        NewFileInDirection,
+        OpenTerminal,
+        Save,
+        SaveAll,
+        SwapPaneInDirection,
+    ]
+);
+
 #[derive(Deserialize)]
 pub struct Toast {
     id: usize,
@@ -200,7 +214,7 @@ impl Clone for Toast {
     }
 }
 
-#[derive(Debug, Default, Clone, Deserialize, PartialEq, Action)]
+#[derive(Debug, Default, Clone, Deserialize, PartialEq)]
 pub struct OpenTerminal {
     pub working_directory: PathBuf,
 }

crates/zed_actions2/src/lib.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::Action;
+use gpui::impl_actions;
 use serde::Deserialize;
 
 // If the zed binary doesn't use anything in this crate, it will be optimized away
@@ -10,12 +10,14 @@ use serde::Deserialize;
 // https://github.com/mmastrac/rust-ctor/issues/280
 pub fn init() {}
 
-#[derive(Clone, PartialEq, Deserialize, Action)]
+#[derive(Clone, PartialEq, Deserialize)]
 pub struct OpenBrowser {
     pub url: String,
 }
 
-#[derive(Clone, PartialEq, Deserialize, Action)]
+#[derive(Clone, PartialEq, Deserialize)]
 pub struct OpenZedURL {
     pub url: String,
 }
+
+impl_actions!(zed, [OpenBrowser, OpenZedURL]);