Fix boxed cloning of `AnyAction`

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

gpui/src/app.rs    | 43 +++++++++++--------------------------------
gpui/src/keymap.rs | 10 +++++-----
2 files changed, 16 insertions(+), 37 deletions(-)

Detailed changes

gpui/src/app.rs 🔗

@@ -94,41 +94,16 @@ pub trait UpdateView {
 
 pub trait Action: 'static + AnyAction {
     type Argument: 'static + Clone;
-
-    const NAME: &'static str;
 }
 
 pub trait AnyAction {
     fn id(&self) -> TypeId;
+    fn name(&self) -> &'static str;
     fn as_any(&self) -> &dyn Any;
     fn boxed_clone(&self) -> Box<dyn AnyAction>;
     fn boxed_clone_as_any(&self) -> Box<dyn Any>;
 }
 
-impl Action for () {
-    type Argument = ();
-
-    const NAME: &'static str = "()";
-}
-
-impl AnyAction for () {
-    fn id(&self) -> TypeId {
-        TypeId::of::<()>()
-    }
-
-    fn as_any(&self) -> &dyn Any {
-        self
-    }
-
-    fn boxed_clone(&self) -> Box<dyn AnyAction> {
-        Box::new(())
-    }
-
-    fn boxed_clone_as_any(&self) -> Box<dyn Any> {
-        Box::new(())
-    }
-}
-
 #[macro_export]
 macro_rules! action {
     ($name:ident, $arg:ty) => {
@@ -137,8 +112,6 @@ macro_rules! action {
 
         impl $crate::Action for $name {
             type Argument = $arg;
-
-            const NAME: &'static str = stringify!($name);
         }
 
         impl $crate::AnyAction for $name {
@@ -146,6 +119,10 @@ macro_rules! action {
                 std::any::TypeId::of::<$name>()
             }
 
+            fn name(&self) -> &'static str {
+                stringify!($name)
+            }
+
             fn as_any(&self) -> &dyn std::any::Any {
                 self
             }
@@ -166,8 +143,6 @@ macro_rules! action {
 
         impl $crate::Action for $name {
             type Argument = ();
-
-            const NAME: &'static str = stringify!($name);
         }
 
         impl $crate::AnyAction for $name {
@@ -175,16 +150,20 @@ macro_rules! action {
                 std::any::TypeId::of::<$name>()
             }
 
+            fn name(&self) -> &'static str {
+                stringify!($name)
+            }
+
             fn as_any(&self) -> &dyn std::any::Any {
                 self
             }
 
             fn boxed_clone(&self) -> Box<dyn $crate::AnyAction> {
-                Box::new(())
+                Box::new(self.clone())
             }
 
             fn boxed_clone_as_any(&self) -> Box<dyn std::any::Any> {
-                Box::new(())
+                Box::new(self.clone())
             }
         }
     };

gpui/src/keymap.rs 🔗

@@ -455,23 +455,23 @@ mod tests {
         assert_eq!(matcher.test_keystroke("a", 1, &ctx_a), Some(A("x")));
 
         // Multi-keystroke match
-        assert_eq!(matcher.test_keystroke::<()>("a", 1, &ctx_b), None);
+        assert_eq!(matcher.test_keystroke::<A>("a", 1, &ctx_b), None);
         assert_eq!(matcher.test_keystroke("b", 1, &ctx_b), Some(Ab));
 
         // Failed matches don't interfere with matching subsequent keys
-        assert_eq!(matcher.test_keystroke::<()>("x", 1, &ctx_a), None);
+        assert_eq!(matcher.test_keystroke::<A>("x", 1, &ctx_a), None);
         assert_eq!(matcher.test_keystroke("a", 1, &ctx_a), Some(A("x")));
 
         // Pending keystrokes are cleared when the context changes
-        assert_eq!(matcher.test_keystroke::<()>("a", 1, &ctx_b), None);
+        assert_eq!(matcher.test_keystroke::<A>("a", 1, &ctx_b), None);
         assert_eq!(matcher.test_keystroke("b", 1, &ctx_a), Some(B));
 
         let mut ctx_c = Context::default();
         ctx_c.set.insert("c".into());
 
         // Pending keystrokes are maintained per-view
-        assert_eq!(matcher.test_keystroke::<()>("a", 1, &ctx_b), None);
-        assert_eq!(matcher.test_keystroke::<()>("a", 2, &ctx_c), None);
+        assert_eq!(matcher.test_keystroke::<A>("a", 1, &ctx_b), None);
+        assert_eq!(matcher.test_keystroke::<A>("a", 2, &ctx_c), None);
         assert_eq!(matcher.test_keystroke("b", 1, &ctx_b), Some(Ab));
 
         Ok(())