WIP

Nathan Sobo created

Change summary

gpui/src/app.rs                   | 279 ++++++++++++++++----------
gpui/src/keymap.rs                |  85 +++----
gpui/src/platform.rs              |   4 
gpui/src/platform/mac/platform.rs |  17 
gpui/src/platform/test.rs         |   4 
gpui/src/presenter.rs             |  30 +-
zed/src/editor.rs                 | 339 +++++++++++++-------------------
zed/src/editor/element.rs         |   8 
8 files changed, 378 insertions(+), 388 deletions(-)

Detailed changes

gpui/src/app.rs 🔗

@@ -92,6 +92,104 @@ pub trait UpdateView {
         F: FnOnce(&mut T, &mut ViewContext<T>) -> S;
 }
 
+pub trait Action: 'static + AnyAction {
+    type Argument: 'static + Clone;
+
+    const NAME: &'static str;
+}
+
+pub trait AnyAction {
+    fn id(&self) -> TypeId;
+    fn arg_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 arg_as_any(&self) -> &dyn Any {
+        &()
+    }
+
+    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) => {
+        #[derive(Clone, Debug, Eq, PartialEq)]
+        pub struct $name(pub $arg);
+
+        impl $crate::Action for $name {
+            type Argument = $arg;
+
+            const NAME: &'static str = stringify!($name);
+        }
+
+        impl $crate::AnyAction for $name {
+            fn id(&self) -> std::any::TypeId {
+                std::any::TypeId::of::<$name>()
+            }
+
+            fn arg_as_any(&self) -> &dyn std::any::Any {
+                &self.0
+            }
+
+            fn boxed_clone(&self) -> Box<dyn $crate::AnyAction> {
+                Box::new(self.clone())
+            }
+
+            fn boxed_clone_as_any(&self) -> Box<dyn std::any::Any> {
+                Box::new(self.clone())
+            }
+        }
+    };
+
+    ($name:ident) => {
+        #[derive(Clone, Debug, Eq, PartialEq)]
+        pub struct $name;
+
+        impl $crate::Action for $name {
+            type Argument = ();
+
+            const NAME: &'static str = stringify!($name);
+        }
+
+        impl $crate::AnyAction for $name {
+            fn id(&self) -> std::any::TypeId {
+                std::any::TypeId::of::<$name>()
+            }
+
+            fn arg_as_any(&self) -> &dyn std::any::Any {
+                &()
+            }
+
+            fn boxed_clone(&self) -> Box<dyn $crate::AnyAction> {
+                Box::new(())
+            }
+
+            fn boxed_clone_as_any(&self) -> Box<dyn std::any::Any> {
+                Box::new(())
+            }
+        }
+    };
+}
+
 pub struct Menu<'a> {
     pub name: &'a str,
     pub items: Vec<MenuItem<'a>>,
@@ -101,8 +199,7 @@ pub enum MenuItem<'a> {
     Action {
         name: &'a str,
         keystroke: Option<&'a str>,
-        action: &'a str,
-        arg: Option<Box<dyn Any + 'static>>,
+        action: Box<dyn AnyAction>,
     },
     Separator,
 }
@@ -136,19 +233,19 @@ impl App {
         ))));
 
         let cx = app.0.clone();
-        foreground_platform.on_menu_command(Box::new(move |command, arg| {
+        foreground_platform.on_menu_command(Box::new(move |action| {
             let mut cx = cx.borrow_mut();
             if let Some(key_window_id) = cx.cx.platform.key_window_id() {
                 if let Some((presenter, _)) = cx.presenters_and_platform_windows.get(&key_window_id)
                 {
                     let presenter = presenter.clone();
                     let path = presenter.borrow().dispatch_path(cx.as_ref());
-                    cx.dispatch_action_any(key_window_id, &path, command, arg.unwrap_or(&()));
+                    cx.dispatch_action_any(key_window_id, &path, action);
                 } else {
-                    cx.dispatch_global_action_any(command, arg.unwrap_or(&()));
+                    cx.dispatch_global_action_any(action);
                 }
             } else {
-                cx.dispatch_global_action_any(command, arg.unwrap_or(&()));
+                cx.dispatch_global_action_any(action);
             }
         }));
 
@@ -258,23 +355,19 @@ impl TestAppContext {
         cx
     }
 
-    pub fn dispatch_action<T: 'static + Any>(
+    pub fn dispatch_action<A: Action>(
         &self,
         window_id: usize,
         responder_chain: Vec<usize>,
-        name: &str,
-        arg: T,
+        action: A,
     ) {
-        self.cx.borrow_mut().dispatch_action_any(
-            window_id,
-            &responder_chain,
-            name,
-            Box::new(arg).as_ref(),
-        );
+        self.cx
+            .borrow_mut()
+            .dispatch_action_any(window_id, &responder_chain, &action);
     }
 
-    pub fn dispatch_global_action<T: 'static + Any>(&self, name: &str, arg: T) {
-        self.cx.borrow_mut().dispatch_global_action(name, arg);
+    pub fn dispatch_global_action<A: Action>(&self, action: A) {
+        self.cx.borrow_mut().dispatch_global_action(action);
     }
 
     pub fn dispatch_keystroke(
@@ -563,17 +656,17 @@ impl ReadViewWith for TestAppContext {
 }
 
 type ActionCallback =
-    dyn FnMut(&mut dyn AnyView, &dyn Any, &mut MutableAppContext, usize, usize) -> bool;
+    dyn FnMut(&mut dyn AnyView, &dyn AnyAction, &mut MutableAppContext, usize, usize) -> bool;
 
-type GlobalActionCallback = dyn FnMut(&dyn Any, &mut MutableAppContext);
+type GlobalActionCallback = dyn FnMut(&dyn AnyAction, &mut MutableAppContext);
 
 pub struct MutableAppContext {
     weak_self: Option<rc::Weak<RefCell<Self>>>,
     foreground_platform: Rc<dyn platform::ForegroundPlatform>,
     assets: Arc<AssetCache>,
     cx: AppContext,
-    actions: HashMap<TypeId, HashMap<String, Vec<Box<ActionCallback>>>>,
-    global_actions: HashMap<String, Vec<Box<GlobalActionCallback>>>,
+    actions: HashMap<TypeId, HashMap<TypeId, Vec<Box<ActionCallback>>>>,
+    global_actions: HashMap<TypeId, Vec<Box<GlobalActionCallback>>>,
     keystroke_matcher: keymap::Matcher,
     next_entity_id: usize,
     next_window_id: usize,
@@ -663,69 +756,53 @@ impl MutableAppContext {
             .map(|debug_elements| debug_elements(&self.cx))
     }
 
-    pub fn add_action<S, V, T, F>(&mut self, name: S, mut handler: F)
+    pub fn add_action<A, V, F>(&mut self, mut handler: F)
     where
-        S: Into<String>,
+        A: Action,
         V: View,
-        T: Any,
-        F: 'static + FnMut(&mut V, &T, &mut ViewContext<V>),
+        F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>),
     {
-        let name = name.into();
-        let name_clone = name.clone();
         let handler = Box::new(
             move |view: &mut dyn AnyView,
-                  arg: &dyn Any,
+                  action: &dyn AnyAction,
                   cx: &mut MutableAppContext,
                   window_id: usize,
                   view_id: usize| {
-                match arg.downcast_ref() {
-                    Some(arg) => {
-                        let mut cx = ViewContext::new(cx, window_id, view_id);
-                        handler(
-                            view.as_any_mut()
-                                .downcast_mut()
-                                .expect("downcast is type safe"),
-                            arg,
-                            &mut cx,
-                        );
-                        cx.halt_action_dispatch
-                    }
-                    None => {
-                        log::error!("Could not downcast argument for action {}", name_clone);
-                        false
-                    }
-                }
+                let arg = action.arg_as_any().downcast_ref().unwrap();
+                let mut cx = ViewContext::new(cx, window_id, view_id);
+                handler(
+                    view.as_any_mut()
+                        .downcast_mut()
+                        .expect("downcast is type safe"),
+                    arg,
+                    &mut cx,
+                );
+                cx.halt_action_dispatch
             },
         );
 
         self.actions
             .entry(TypeId::of::<V>())
             .or_default()
-            .entry(name)
+            .entry(TypeId::of::<A>())
             .or_default()
             .push(handler);
     }
 
-    pub fn add_global_action<S, T, F>(&mut self, name: S, mut handler: F)
+    pub fn add_global_action<A, F>(&mut self, mut handler: F)
     where
-        S: Into<String>,
-        T: 'static + Any,
-        F: 'static + FnMut(&T, &mut MutableAppContext),
+        A: Action,
+        F: 'static + FnMut(&A, &mut MutableAppContext),
     {
-        let name = name.into();
-        let name_clone = name.clone();
-        let handler = Box::new(move |arg: &dyn Any, cx: &mut MutableAppContext| {
-            if let Some(arg) = arg.downcast_ref() {
-                handler(arg, cx);
-            } else {
-                log::error!(
-                    "Could not downcast argument for global action {}",
-                    name_clone
-                );
-            }
+        let handler = Box::new(move |action: &dyn AnyAction, cx: &mut MutableAppContext| {
+            let arg = action.arg_as_any().downcast_ref().unwrap();
+            handler(arg, cx);
         });
 
-        self.global_actions.entry(name).or_default().push(handler);
+        self.global_actions
+            .entry(TypeId::of::<A>())
+            .or_default()
+            .push(handler);
     }
 
     pub fn window_ids(&self) -> impl Iterator<Item = usize> + '_ {
@@ -838,22 +915,20 @@ impl MutableAppContext {
         self.pending_effects.extend(notifications);
     }
 
-    pub fn dispatch_action<T: 'static + Any>(
+    pub fn dispatch_action<A: Action>(
         &mut self,
         window_id: usize,
         responder_chain: Vec<usize>,
-        name: &str,
-        arg: T,
+        action: &A,
     ) {
-        self.dispatch_action_any(window_id, &responder_chain, name, Box::new(arg).as_ref());
+        self.dispatch_action_any(window_id, &responder_chain, action);
     }
 
     pub(crate) fn dispatch_action_any(
         &mut self,
         window_id: usize,
         path: &[usize],
-        name: &str,
-        arg: &dyn Any,
+        action: &dyn AnyAction,
     ) -> bool {
         self.pending_flushes += 1;
         let mut halted_dispatch = false;
@@ -865,10 +940,11 @@ impl MutableAppContext {
                 if let Some((name, mut handlers)) = self
                     .actions
                     .get_mut(&type_id)
-                    .and_then(|h| h.remove_entry(name))
+                    .and_then(|h| h.remove_entry(&action.id()))
                 {
                     for handler in handlers.iter_mut().rev() {
-                        let halt_dispatch = handler(view.as_mut(), arg, self, window_id, *view_id);
+                        let halt_dispatch =
+                            handler(view.as_mut(), action, self, window_id, *view_id);
                         if halt_dispatch {
                             halted_dispatch = true;
                             break;
@@ -889,22 +965,22 @@ impl MutableAppContext {
         }
 
         if !halted_dispatch {
-            self.dispatch_global_action_any(name, arg);
+            self.dispatch_global_action_any(action);
         }
 
         self.flush_effects();
         halted_dispatch
     }
 
-    pub fn dispatch_global_action<T: 'static + Any>(&mut self, name: &str, arg: T) {
-        self.dispatch_global_action_any(name, Box::new(arg).as_ref());
+    pub fn dispatch_global_action<A: Action>(&mut self, action: A) {
+        self.dispatch_global_action_any(&action);
     }
 
-    fn dispatch_global_action_any(&mut self, name: &str, arg: &dyn Any) {
-        if let Some((name, mut handlers)) = self.global_actions.remove_entry(name) {
+    fn dispatch_global_action_any(&mut self, action: &dyn AnyAction) {
+        if let Some((name, mut handlers)) = self.global_actions.remove_entry(&action.id()) {
             self.pending_flushes += 1;
             for handler in handlers.iter_mut().rev() {
-                handler(arg, self);
+                handler(action, self);
             }
             self.global_actions.insert(name, handlers);
             self.flush_effects();
@@ -943,13 +1019,9 @@ impl MutableAppContext {
             {
                 MatchResult::None => {}
                 MatchResult::Pending => pending = true,
-                MatchResult::Action { name, arg } => {
-                    if self.dispatch_action_any(
-                        window_id,
-                        &responder_chain[0..=i],
-                        &name,
-                        arg.as_ref().map(|arg| arg.as_ref()).unwrap_or(&()),
-                    ) {
+                MatchResult::Action(action) => {
+                    if self.dispatch_action_any(window_id, &responder_chain[0..=i], action.as_ref())
+                    {
                         return Ok(true);
                     }
                 }
@@ -3575,31 +3647,29 @@ mod tests {
             }
         }
 
-        struct ActionArg {
-            foo: String,
-        }
+        action!(Action, &'static str);
 
         let actions = Rc::new(RefCell::new(Vec::new()));
 
         let actions_clone = actions.clone();
-        cx.add_global_action("action", move |_: &ActionArg, _: &mut MutableAppContext| {
+        cx.add_global_action(move |_: &Action, _: &mut MutableAppContext| {
             actions_clone.borrow_mut().push("global a".to_string());
         });
 
         let actions_clone = actions.clone();
-        cx.add_global_action("action", move |_: &ActionArg, _: &mut MutableAppContext| {
+        cx.add_global_action(move |_: &Action, _: &mut MutableAppContext| {
             actions_clone.borrow_mut().push("global b".to_string());
         });
 
         let actions_clone = actions.clone();
-        cx.add_action("action", move |view: &mut ViewA, arg: &ActionArg, cx| {
-            assert_eq!(arg.foo, "bar");
+        cx.add_action(move |view: &mut ViewA, action: &Action, cx| {
+            assert_eq!(action.0, "bar");
             cx.propagate_action();
             actions_clone.borrow_mut().push(format!("{} a", view.id));
         });
 
         let actions_clone = actions.clone();
-        cx.add_action("action", move |view: &mut ViewA, _: &ActionArg, cx| {
+        cx.add_action(move |view: &mut ViewA, _: &Action, cx| {
             if view.id != 1 {
                 cx.propagate_action();
             }
@@ -3607,13 +3677,13 @@ mod tests {
         });
 
         let actions_clone = actions.clone();
-        cx.add_action("action", move |view: &mut ViewB, _: &ActionArg, cx| {
+        cx.add_action(move |view: &mut ViewB, action: &Action, cx| {
             cx.propagate_action();
             actions_clone.borrow_mut().push(format!("{} c", view.id));
         });
 
         let actions_clone = actions.clone();
-        cx.add_action("action", move |view: &mut ViewB, _: &ActionArg, cx| {
+        cx.add_action(move |view: &mut ViewB, action: &Action, cx| {
             cx.propagate_action();
             actions_clone.borrow_mut().push(format!("{} d", view.id));
         });
@@ -3626,8 +3696,7 @@ mod tests {
         cx.dispatch_action(
             window_id,
             vec![view_1.id(), view_2.id(), view_3.id(), view_4.id()],
-            "action",
-            ActionArg { foo: "bar".into() },
+            &Action("bar"),
         );
 
         assert_eq!(
@@ -3640,8 +3709,7 @@ mod tests {
         cx.dispatch_action(
             window_id,
             vec![view_2.id(), view_3.id(), view_4.id()],
-            "action",
-            ActionArg { foo: "bar".into() },
+            &Action("bar"),
         );
 
         assert_eq!(
@@ -3654,10 +3722,7 @@ mod tests {
     fn test_dispatch_keystroke(cx: &mut MutableAppContext) {
         use std::cell::Cell;
 
-        #[derive(Clone)]
-        struct ActionArg {
-            key: String,
-        }
+        action!(Action, &'static str);
 
         struct View {
             id: usize,
@@ -3704,16 +3769,18 @@ mod tests {
 
         // This keymap's only binding dispatches an action on view 2 because that view will have
         // "a" and "b" in its context, but not "c".
-        let binding = keymap::Binding::new("a", "action", Some("a && b && !c"))
-            .with_arg(ActionArg { key: "a".into() });
-        cx.add_bindings(vec![binding]);
+        cx.add_bindings(vec![keymap::Binding::new(
+            "a",
+            Action("a"),
+            Some("a && b && !c"),
+        )]);
 
         let handled_action = Rc::new(Cell::new(false));
         let handled_action_clone = handled_action.clone();
-        cx.add_action("action", move |view: &mut View, arg: &ActionArg, _| {
+        cx.add_action(move |view: &mut View, action: &Action, _| {
             handled_action_clone.set(true);
             assert_eq!(view.id, 2);
-            assert_eq!(arg.key, "a");
+            assert_eq!(action.0, "a");
         });
 
         cx.dispatch_keystroke(

gpui/src/keymap.rs 🔗

@@ -5,6 +5,8 @@ use std::{
 };
 use tree_sitter::{Language, Node, Parser};
 
+use crate::{Action, AnyAction};
+
 extern "C" {
     fn tree_sitter_context_predicate() -> Language;
 }
@@ -24,8 +26,7 @@ pub struct Keymap(Vec<Binding>);
 
 pub struct Binding {
     keystrokes: Vec<Keystroke>,
-    action: String,
-    action_arg: Option<Box<dyn ActionArg>>,
+    action: Box<dyn AnyAction>,
     context: Option<ContextPredicate>,
 }
 
@@ -70,10 +71,7 @@ where
 pub enum MatchResult {
     None,
     Pending,
-    Action {
-        name: String,
-        arg: Option<Box<dyn Any>>,
-    },
+    Action(Box<dyn AnyAction>),
 }
 
 impl Matcher {
@@ -117,10 +115,7 @@ impl Matcher {
             {
                 if binding.keystrokes.len() == pending.keystrokes.len() {
                     self.pending.remove(&view_id);
-                    return MatchResult::Action {
-                        name: binding.action.clone(),
-                        arg: binding.action_arg.as_ref().map(|arg| (*arg).boxed_clone()),
-                    };
+                    return MatchResult::Action(binding.action.boxed_clone());
                 } else {
                     retain_pending = true;
                     pending.context = Some(cx.clone());
@@ -153,19 +148,26 @@ impl Keymap {
     }
 }
 
+mod menu {
+    use crate::action;
+
+    action!(SelectPrev);
+    action!(SelectNext);
+}
+
 impl Default for Keymap {
     fn default() -> Self {
         Self(vec![
-            Binding::new("up", "menu:select_prev", Some("menu")),
-            Binding::new("ctrl-p", "menu:select_prev", Some("menu")),
-            Binding::new("down", "menu:select_next", Some("menu")),
-            Binding::new("ctrl-n", "menu:select_next", Some("menu")),
+            Binding::new("up", menu::SelectPrev, Some("menu")),
+            Binding::new("ctrl-p", menu::SelectPrev, Some("menu")),
+            Binding::new("down", menu::SelectNext, Some("menu")),
+            Binding::new("ctrl-n", menu::SelectNext, Some("menu")),
         ])
     }
 }
 
 impl Binding {
-    pub fn new<S: Into<String>>(keystrokes: &str, action: S, context: Option<&str>) -> Self {
+    pub fn new<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> Self {
         let context = if let Some(context) = context {
             Some(ContextPredicate::parse(context).unwrap())
         } else {
@@ -177,16 +179,10 @@ impl Binding {
                 .split_whitespace()
                 .map(|key| Keystroke::parse(key).unwrap())
                 .collect(),
-            action: action.into(),
-            action_arg: None,
+            action: Box::new(action),
             context,
         }
     }
-
-    pub fn with_arg<T: 'static + Any + Clone>(mut self, arg: T) -> Self {
-        self.action_arg = Some(Box::new(arg));
-        self
-    }
 }
 
 impl Keystroke {
@@ -328,6 +324,8 @@ impl ContextPredicate {
 
 #[cfg(test)]
 mod tests {
+    use crate::action;
+
     use super::*;
 
     #[test]
@@ -417,15 +415,19 @@ mod tests {
 
     #[test]
     fn test_matcher() -> anyhow::Result<()> {
+        action!(A, &'static str);
+        action!(B);
+        action!(Ab);
+
         #[derive(Clone, Debug, Eq, PartialEq)]
         struct ActionArg {
             a: &'static str,
         }
 
         let keymap = Keymap(vec![
-            Binding::new("a", "a", Some("a")).with_arg(ActionArg { a: "b" }),
-            Binding::new("b", "b", Some("a")),
-            Binding::new("a b", "a_b", Some("a || b")),
+            Binding::new("a", A("x"), Some("a")),
+            Binding::new("b", B, Some("a")),
+            Binding::new("a b", Ab, Some("a || b")),
         ]);
 
         let mut ctx_a = Context::default();
@@ -437,31 +439,19 @@ mod tests {
         let mut matcher = Matcher::new(keymap);
 
         // Basic match
-        assert_eq!(
-            matcher.test_keystroke("a", 1, &ctx_a),
-            Some(("a".to_string(), Some(ActionArg { a: "b" })))
-        );
+        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::<()>("b", 1, &ctx_b),
-            Some(("a_b".to_string(), 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", 1, &ctx_a),
-            Some(("a".to_string(), Some(ActionArg { a: "b" })))
-        );
+        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::<()>("b", 1, &ctx_a),
-            Some(("b".to_string(), None))
-        );
+        assert_eq!(matcher.test_keystroke("b", 1, &ctx_a), Some(B));
 
         let mut ctx_c = Context::default();
         ctx_c.set.insert("c".into());
@@ -469,25 +459,22 @@ mod tests {
         // 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::<()>("b", 1, &ctx_b),
-            Some(("a_b".to_string(), None))
-        );
+        assert_eq!(matcher.test_keystroke("b", 1, &ctx_b), Some(Ab));
 
         Ok(())
     }
 
     impl Matcher {
-        fn test_keystroke<A: Any + Clone>(
+        fn test_keystroke<A: Action>(
             &mut self,
             keystroke: &str,
             view_id: usize,
             cx: &Context,
-        ) -> Option<(String, Option<A>)> {
-            if let MatchResult::Action { name, arg } =
+        ) -> Option<A> {
+            if let MatchResult::Action(action) =
                 self.push_keystroke(Keystroke::parse(keystroke).unwrap(), view_id, cx)
             {
-                Some((name, arg.and_then(|arg| arg.downcast_ref::<A>().cloned())))
+                Some(*action.boxed_clone_as_any().downcast().unwrap())
             } else {
                 None
             }

gpui/src/platform.rs 🔗

@@ -16,7 +16,7 @@ use crate::{
         vector::{vec2f, Vector2F},
     },
     text_layout::LineLayout,
-    ClipboardItem, Menu, Scene,
+    AnyAction, ClipboardItem, Menu, Scene,
 };
 use async_task::Runnable;
 pub use event::Event;
@@ -56,7 +56,7 @@ pub(crate) trait ForegroundPlatform {
     fn on_open_files(&self, callback: Box<dyn FnMut(Vec<PathBuf>)>);
     fn run(&self, on_finish_launching: Box<dyn FnOnce() -> ()>);
 
-    fn on_menu_command(&self, callback: Box<dyn FnMut(&str, Option<&dyn Any>)>);
+    fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn AnyAction)>);
     fn set_menus(&self, menus: Vec<Menu>);
     fn prompt_for_paths(
         &self,

gpui/src/platform/mac/platform.rs 🔗

@@ -1,5 +1,7 @@
 use super::{BoolExt as _, Dispatcher, FontSystem, Window};
-use crate::{executor, keymap::Keystroke, platform, ClipboardItem, Event, Menu, MenuItem};
+use crate::{
+    executor, keymap::Keystroke, platform, AnyAction, ClipboardItem, Event, Menu, MenuItem,
+};
 use block::ConcreteBlock;
 use cocoa::{
     appkit::{
@@ -90,10 +92,10 @@ pub struct MacForegroundPlatformState {
     become_active: Option<Box<dyn FnMut()>>,
     resign_active: Option<Box<dyn FnMut()>>,
     event: Option<Box<dyn FnMut(crate::Event) -> bool>>,
-    menu_command: Option<Box<dyn FnMut(&str, Option<&dyn Any>)>>,
+    menu_command: Option<Box<dyn FnMut(&dyn AnyAction)>>,
     open_files: Option<Box<dyn FnMut(Vec<PathBuf>)>>,
     finish_launching: Option<Box<dyn FnOnce() -> ()>>,
-    menu_actions: Vec<(String, Option<Box<dyn Any>>)>,
+    menu_actions: Vec<Box<dyn AnyAction>>,
 }
 
 impl MacForegroundPlatform {
@@ -121,7 +123,6 @@ impl MacForegroundPlatform {
                         name,
                         keystroke,
                         action,
-                        arg,
                     } => {
                         if let Some(keystroke) = keystroke {
                             let keystroke = Keystroke::parse(keystroke).unwrap_or_else(|err| {
@@ -162,7 +163,7 @@ impl MacForegroundPlatform {
 
                         let tag = state.menu_actions.len() as NSInteger;
                         let _: () = msg_send![item, setTag: tag];
-                        state.menu_actions.push((action.to_string(), arg));
+                        state.menu_actions.push(action);
                     }
                 }
 
@@ -215,7 +216,7 @@ impl platform::ForegroundPlatform for MacForegroundPlatform {
         }
     }
 
-    fn on_menu_command(&self, callback: Box<dyn FnMut(&str, Option<&dyn Any>)>) {
+    fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn AnyAction)>) {
         self.0.borrow_mut().menu_command = Some(callback);
     }
 
@@ -623,8 +624,8 @@ extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
         if let Some(mut callback) = platform.menu_command.take() {
             let tag: NSInteger = msg_send![item, tag];
             let index = tag as usize;
-            if let Some((action, arg)) = platform.menu_actions.get(index) {
-                callback(action, arg.as_ref().map(Box::as_ref));
+            if let Some(action) = platform.menu_actions.get(index) {
+                callback(action.as_ref());
             }
             platform.menu_command = Some(callback);
         }

gpui/src/platform/test.rs 🔗

@@ -1,4 +1,4 @@
-use crate::ClipboardItem;
+use crate::{AnyAction, ClipboardItem};
 use parking_lot::Mutex;
 use pathfinder_geometry::vector::Vector2F;
 use std::{
@@ -62,7 +62,7 @@ impl super::ForegroundPlatform for ForegroundPlatform {
         unimplemented!()
     }
 
-    fn on_menu_command(&self, _: Box<dyn FnMut(&str, Option<&dyn Any>)>) {}
+    fn on_menu_command(&self, _: Box<dyn FnMut(&dyn AnyAction)>) {}
 
     fn set_menus(&self, _: Vec<crate::Menu>) {}
 

gpui/src/presenter.rs 🔗

@@ -5,12 +5,11 @@ use crate::{
     json::{self, ToJson},
     platform::Event,
     text_layout::TextLayoutCache,
-    AssetCache, ElementBox, Scene,
+    Action, AnyAction, AssetCache, ElementBox, Scene,
 };
 use pathfinder_geometry::vector::{vec2f, Vector2F};
 use serde_json::json;
 use std::{
-    any::Any,
     collections::{HashMap, HashSet},
     sync::Arc,
 };
@@ -144,7 +143,7 @@ impl Presenter {
 
             let mut event_cx = EventContext {
                 rendered_views: &mut self.rendered_views,
-                actions: Default::default(),
+                dispatched_actions: Default::default(),
                 font_cache: &self.font_cache,
                 text_layout_cache: &self.text_layout_cache,
                 view_stack: Default::default(),
@@ -154,18 +153,13 @@ impl Presenter {
             event_cx.dispatch_event(root_view_id, &event);
 
             let invalidated_views = event_cx.invalidated_views;
-            let actions = event_cx.actions;
+            let dispatch_directives = event_cx.dispatched_actions;
 
             for view_id in invalidated_views {
                 cx.notify_view(self.window_id, view_id);
             }
-            for action in actions {
-                cx.dispatch_action_any(
-                    self.window_id,
-                    &action.path,
-                    action.name,
-                    action.arg.as_ref(),
-                );
+            for directive in dispatch_directives {
+                cx.dispatch_action_any(self.window_id, &directive.path, directive.action.as_ref());
             }
         }
     }
@@ -183,10 +177,9 @@ impl Presenter {
     }
 }
 
-pub struct ActionToDispatch {
+pub struct DispatchDirective {
     pub path: Vec<usize>,
-    pub name: &'static str,
-    pub arg: Box<dyn Any>,
+    pub action: Box<dyn AnyAction>,
 }
 
 pub struct LayoutContext<'a> {
@@ -249,7 +242,7 @@ impl<'a> PaintContext<'a> {
 
 pub struct EventContext<'a> {
     rendered_views: &'a mut HashMap<usize, ElementBox>,
-    actions: Vec<ActionToDispatch>,
+    dispatched_actions: Vec<DispatchDirective>,
     pub font_cache: &'a FontCache,
     pub text_layout_cache: &'a TextLayoutCache,
     pub app: &'a mut MutableAppContext,
@@ -270,11 +263,10 @@ impl<'a> EventContext<'a> {
         }
     }
 
-    pub fn dispatch_action<A: 'static + Any>(&mut self, name: &'static str, arg: A) {
-        self.actions.push(ActionToDispatch {
+    pub fn dispatch_action<A: Action>(&mut self, action: A) {
+        self.dispatched_actions.push(DispatchDirective {
             path: self.view_stack.clone(),
-            name,
-            arg: Box::new(arg),
+            action: Box::new(action),
         });
     }
 

zed/src/editor.rs 🔗

@@ -16,7 +16,7 @@ pub use display_map::DisplayPoint;
 use display_map::*;
 pub use element::*;
 use gpui::{
-    color::Color, font_cache::FamilyId, fonts::Properties as FontProperties,
+    action, color::Color, font_cache::FamilyId, fonts::Properties as FontProperties,
     geometry::vector::Vector2F, keymap::Binding, text_layout, AppContext, ClipboardItem, Element,
     ElementBox, Entity, FontCache, ModelHandle, MutableAppContext, RenderContext, Task,
     TextLayoutCache, View, ViewContext, WeakViewHandle,
@@ -40,224 +40,167 @@ use std::{
 const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
 const MAX_LINE_LEN: usize = 1024;
 
+action!(Cancel);
+action!(Backspace);
+action!(Delete);
+action!(Newline);
+action!(Insert, String);
+action!(DeleteLine);
+action!(DeleteToPreviousWordBoundary);
+action!(DeleteToNextWordBoundary);
+action!(DeleteToBeginningOfLine);
+action!(DeleteToEndOfLine);
+action!(CutToEndOfLine);
+action!(DuplicateLine);
+action!(MoveLineUp);
+action!(MoveLineDown);
+action!(Cut);
+action!(Copy);
+action!(Paste);
+action!(Undo);
+action!(Redo);
+action!(MoveUp);
+action!(MoveDown);
+action!(MoveLeft);
+action!(MoveRight);
+action!(MoveToPreviousWordBoundary);
+action!(MoveToNextWordBoundary);
+action!(MoveToBeginningOfLine);
+action!(MoveToEndOfLine);
+action!(MoveToBeginning);
+action!(MoveToEnd);
+action!(SelectUp);
+action!(SelectDown);
+action!(SelectLeft);
+action!(SelectRight);
+action!(SelectToPreviousWordBoundary);
+action!(SelectToNextWordBoundary);
+action!(SelectToBeginningOfLine, bool);
+action!(SelectToEndOfLine);
+action!(SelectToBeginning);
+action!(SelectToEnd);
+action!(SelectAll);
+action!(SelectLine);
+action!(SplitSelectionIntoLines);
+action!(AddSelectionAbove);
+action!(AddSelectionBelow);
+action!(SelectLargerSyntaxNode);
+action!(SelectSmallerSyntaxNode);
+action!(MoveToEnclosingBracket);
+action!(PageUp);
+action!(PageDown);
+action!(Fold);
+action!(Unfold);
+action!(FoldSelectedRanges);
+action!(Scroll, Vector2F);
+action!(Select, SelectPhase);
+
 pub fn init(cx: &mut MutableAppContext) {
     cx.add_bindings(vec![
-        Binding::new("escape", "buffer:cancel", Some("BufferView")),
-        Binding::new("backspace", "buffer:backspace", Some("BufferView")),
-        Binding::new("ctrl-h", "buffer:backspace", Some("BufferView")),
-        Binding::new("delete", "buffer:delete", Some("BufferView")),
-        Binding::new("ctrl-d", "buffer:delete", Some("BufferView")),
-        Binding::new("enter", "buffer:newline", Some("BufferView")),
-        Binding::new("tab", "buffer:insert", Some("BufferView")).with_arg("\t".to_string()),
-        Binding::new("ctrl-shift-K", "buffer:delete_line", Some("BufferView")),
+        Binding::new("escape", Cancel, Some("BufferView")),
+        Binding::new("backspace", Backspace, Some("BufferView")),
+        Binding::new("ctrl-h", Backspace, Some("BufferView")),
+        Binding::new("delete", Delete, Some("BufferView")),
+        Binding::new("ctrl-d", Delete, Some("BufferView")),
+        Binding::new("enter", Newline, Some("BufferView")),
+        Binding::new("tab", Insert("\t".into()), Some("BufferView")),
+        Binding::new("ctrl-shift-K", DeleteLine, Some("BufferView")),
         Binding::new(
             "alt-backspace",
-            "buffer:delete_to_previous_word_boundary",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "alt-h",
-            "buffer:delete_to_previous_word_boundary",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "alt-delete",
-            "buffer:delete_to_next_word_boundary",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "alt-d",
-            "buffer:delete_to_next_word_boundary",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-backspace",
-            "buffer:delete_to_beginning_of_line",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-delete",
-            "buffer:delete_to_end_of_line",
-            Some("BufferView"),
-        ),
-        Binding::new("ctrl-k", "buffer:cut_to_end_of_line", Some("BufferView")),
-        Binding::new("cmd-shift-D", "buffer:duplicate_line", Some("BufferView")),
-        Binding::new("ctrl-cmd-up", "buffer:move_line_up", Some("BufferView")),
-        Binding::new("ctrl-cmd-down", "buffer:move_line_down", Some("BufferView")),
-        Binding::new("cmd-x", "buffer:cut", Some("BufferView")),
-        Binding::new("cmd-c", "buffer:copy", Some("BufferView")),
-        Binding::new("cmd-v", "buffer:paste", Some("BufferView")),
-        Binding::new("cmd-z", "buffer:undo", Some("BufferView")),
-        Binding::new("cmd-shift-Z", "buffer:redo", Some("BufferView")),
-        Binding::new("up", "buffer:move_up", Some("BufferView")),
-        Binding::new("down", "buffer:move_down", Some("BufferView")),
-        Binding::new("left", "buffer:move_left", Some("BufferView")),
-        Binding::new("right", "buffer:move_right", Some("BufferView")),
-        Binding::new("ctrl-p", "buffer:move_up", Some("BufferView")),
-        Binding::new("ctrl-n", "buffer:move_down", Some("BufferView")),
-        Binding::new("ctrl-b", "buffer:move_left", Some("BufferView")),
-        Binding::new("ctrl-f", "buffer:move_right", Some("BufferView")),
-        Binding::new(
-            "alt-left",
-            "buffer:move_to_previous_word_boundary",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "alt-b",
-            "buffer:move_to_previous_word_boundary",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "alt-right",
-            "buffer:move_to_next_word_boundary",
+            DeleteToPreviousWordBoundary,
             Some("BufferView"),
         ),
-        Binding::new(
-            "alt-f",
-            "buffer:move_to_next_word_boundary",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-left",
-            "buffer:move_to_beginning_of_line",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "ctrl-a",
-            "buffer:move_to_beginning_of_line",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-right",
-            "buffer:move_to_end_of_line",
-            Some("BufferView"),
-        ),
-        Binding::new("ctrl-e", "buffer:move_to_end_of_line", Some("BufferView")),
-        Binding::new("cmd-up", "buffer:move_to_beginning", Some("BufferView")),
-        Binding::new("cmd-down", "buffer:move_to_end", Some("BufferView")),
-        Binding::new("shift-up", "buffer:select_up", Some("BufferView")),
-        Binding::new("ctrl-shift-P", "buffer:select_up", Some("BufferView")),
-        Binding::new("shift-down", "buffer:select_down", Some("BufferView")),
-        Binding::new("ctrl-shift-N", "buffer:select_down", Some("BufferView")),
-        Binding::new("shift-left", "buffer:select_left", Some("BufferView")),
-        Binding::new("ctrl-shift-B", "buffer:select_left", Some("BufferView")),
-        Binding::new("shift-right", "buffer:select_right", Some("BufferView")),
-        Binding::new("ctrl-shift-F", "buffer:select_right", Some("BufferView")),
+        Binding::new("alt-h", DeleteToPreviousWordBoundary, Some("BufferView")),
+        Binding::new("alt-delete", DeleteToNextWordBoundary, Some("BufferView")),
+        Binding::new("alt-d", DeleteToNextWordBoundary, Some("BufferView")),
+        Binding::new("cmd-backspace", DeleteToBeginningOfLine, Some("BufferView")),
+        Binding::new("cmd-delete", DeleteToEndOfLine, Some("BufferView")),
+        Binding::new("ctrl-k", CutToEndOfLine, Some("BufferView")),
+        Binding::new("cmd-shift-D", DuplicateLine, Some("BufferView")),
+        Binding::new("ctrl-cmd-up", MoveLineUp, Some("BufferView")),
+        Binding::new("ctrl-cmd-down", MoveLineDown, Some("BufferView")),
+        Binding::new("cmd-x", Cut, Some("BufferView")),
+        Binding::new("cmd-c", Copy, Some("BufferView")),
+        Binding::new("cmd-v", Paste, Some("BufferView")),
+        Binding::new("cmd-z", Undo, Some("BufferView")),
+        Binding::new("cmd-shift-Z", Redo, Some("BufferView")),
+        Binding::new("up", MoveUp, Some("BufferView")),
+        Binding::new("down", MoveDown, Some("BufferView")),
+        Binding::new("left", MoveLeft, Some("BufferView")),
+        Binding::new("right", MoveRight, Some("BufferView")),
+        Binding::new("ctrl-p", MoveUp, Some("BufferView")),
+        Binding::new("ctrl-n", MoveDown, Some("BufferView")),
+        Binding::new("ctrl-b", MoveLeft, Some("BufferView")),
+        Binding::new("ctrl-f", MoveRight, Some("BufferView")),
+        Binding::new("alt-left", MoveToPreviousWordBoundary, Some("BufferView")),
+        Binding::new("alt-b", MoveToPreviousWordBoundary, Some("BufferView")),
+        Binding::new("alt-right", MoveToNextWordBoundary, Some("BufferView")),
+        Binding::new("alt-f", MoveToNextWordBoundary, Some("BufferView")),
+        Binding::new("cmd-left", MoveToBeginningOfLine, Some("BufferView")),
+        Binding::new("ctrl-a", MoveToBeginningOfLine, Some("BufferView")),
+        Binding::new("cmd-right", MoveToEndOfLine, Some("BufferView")),
+        Binding::new("ctrl-e", MoveToEndOfLine, Some("BufferView")),
+        Binding::new("cmd-up", MoveToBeginning, Some("BufferView")),
+        Binding::new("cmd-down", MoveToEnd, Some("BufferView")),
+        Binding::new("shift-up", SelectUp, Some("BufferView")),
+        Binding::new("ctrl-shift-P", SelectUp, Some("BufferView")),
+        Binding::new("shift-down", SelectDown, Some("BufferView")),
+        Binding::new("ctrl-shift-N", SelectDown, Some("BufferView")),
+        Binding::new("shift-left", SelectLeft, Some("BufferView")),
+        Binding::new("ctrl-shift-B", SelectLeft, Some("BufferView")),
+        Binding::new("shift-right", SelectRight, Some("BufferView")),
+        Binding::new("ctrl-shift-F", SelectRight, Some("BufferView")),
         Binding::new(
             "alt-shift-left",
-            "buffer:select_to_previous_word_boundary",
+            SelectToPreviousWordBoundary,
             Some("BufferView"),
         ),
         Binding::new(
             "alt-shift-B",
-            "buffer:select_to_previous_word_boundary",
+            SelectToPreviousWordBoundary,
             Some("BufferView"),
         ),
         Binding::new(
             "alt-shift-right",
-            "buffer:select_to_next_word_boundary",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "alt-shift-F",
-            "buffer:select_to_next_word_boundary",
+            SelectToNextWordBoundary,
             Some("BufferView"),
         ),
+        Binding::new("alt-shift-F", SelectToNextWordBoundary, Some("BufferView")),
         Binding::new(
             "cmd-shift-left",
-            "buffer:select_to_beginning_of_line",
-            Some("BufferView"),
-        )
-        .with_arg(true),
-        Binding::new(
-            "ctrl-shift-A",
-            "buffer:select_to_beginning_of_line",
-            Some("BufferView"),
-        )
-        .with_arg(true),
-        Binding::new(
-            "cmd-shift-right",
-            "buffer:select_to_end_of_line",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "ctrl-shift-E",
-            "buffer:select_to_end_of_line",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-shift-up",
-            "buffer:select_to_beginning",
-            Some("BufferView"),
-        ),
-        Binding::new("cmd-shift-down", "buffer:select_to_end", Some("BufferView")),
-        Binding::new("cmd-a", "buffer:select_all", Some("BufferView")),
-        Binding::new("cmd-l", "buffer:select_line", Some("BufferView")),
-        Binding::new(
-            "cmd-shift-L",
-            "buffer:split_selection_into_lines",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-alt-up",
-            "buffer:add_selection_above",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-ctrl-p",
-            "buffer:add_selection_above",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-alt-down",
-            "buffer:add_selection_below",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "cmd-ctrl-n",
-            "buffer:add_selection_below",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "alt-up",
-            "buffer:select_larger_syntax_node",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "ctrl-w",
-            "buffer:select_larger_syntax_node",
+            SelectToBeginningOfLine(true),
             Some("BufferView"),
         ),
         Binding::new(
-            "alt-down",
-            "buffer:select_smaller_syntax_node",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "ctrl-shift-W",
-            "buffer:select_smaller_syntax_node",
-            Some("BufferView"),
-        ),
-        Binding::new(
-            "ctrl-m",
-            "buffer:move_to_enclosing_bracket",
-            Some("BufferView"),
-        ),
-        Binding::new("pageup", "buffer:page_up", Some("BufferView")),
-        Binding::new("pagedown", "buffer:page_down", Some("BufferView")),
-        Binding::new("alt-cmd-[", "buffer:fold", Some("BufferView")),
-        Binding::new("alt-cmd-]", "buffer:unfold", Some("BufferView")),
-        Binding::new(
-            "alt-cmd-f",
-            "buffer:fold_selected_ranges",
+            "ctrl-shift-A",
+            SelectToBeginningOfLine(true),
             Some("BufferView"),
         ),
+        Binding::new("cmd-shift-right", SelectToEndOfLine, Some("BufferView")),
+        Binding::new("ctrl-shift-E", SelectToEndOfLine, Some("BufferView")),
+        Binding::new("cmd-shift-up", SelectToBeginning, Some("BufferView")),
+        Binding::new("cmd-shift-down", SelectToEnd, Some("BufferView")),
+        Binding::new("cmd-a", SelectAll, Some("BufferView")),
+        Binding::new("cmd-l", SelectLine, Some("BufferView")),
+        Binding::new("cmd-shift-L", SplitSelectionIntoLines, Some("BufferView")),
+        Binding::new("cmd-alt-up", AddSelectionAbove, Some("BufferView")),
+        Binding::new("cmd-ctrl-p", AddSelectionAbove, Some("BufferView")),
+        Binding::new("cmd-alt-down", AddSelectionBelow, Some("BufferView")),
+        Binding::new("cmd-ctrl-n", AddSelectionBelow, Some("BufferView")),
+        Binding::new("alt-up", SelectLargerSyntaxNode, Some("BufferView")),
+        Binding::new("ctrl-w", SelectLargerSyntaxNode, Some("BufferView")),
+        Binding::new("alt-down", SelectSmallerSyntaxNode, Some("BufferView")),
+        Binding::new("ctrl-shift-W", SelectSmallerSyntaxNode, Some("BufferView")),
+        Binding::new("ctrl-m", MoveToEnclosingBracket, Some("BufferView")),
+        Binding::new("pageup", PageUp, Some("BufferView")),
+        Binding::new("pagedown", PageDown, Some("BufferView")),
+        Binding::new("alt-cmd-[", Fold, Some("BufferView")),
+        Binding::new("alt-cmd-]", Unfold, Some("BufferView")),
+        Binding::new("alt-cmd-f", FoldSelectedRanges, Some("BufferView")),
     ]);
 
-    cx.add_action("buffer:scroll", |this: &mut Editor, scroll_position, cx| {
-        this.set_scroll_position(*scroll_position, cx)
-    });
-    cx.add_action("buffer:select", Editor::select);
+    cx.add_action(|this: &mut Editor, action: &Scroll, cx| this.set_scroll_position(action.0, cx));
+    cx.add_action(Editor::select);
     cx.add_action("buffer:cancel", Editor::cancel);
     cx.add_action("buffer:insert", Editor::insert);
     cx.add_action("buffer:newline", Editor::newline);
@@ -357,7 +300,7 @@ pub fn init(cx: &mut MutableAppContext) {
     cx.add_action("buffer:fold_selected_ranges", Editor::fold_selected_ranges);
 }
 
-pub enum SelectAction {
+pub enum SelectPhase {
     Begin {
         position: DisplayPoint,
         add: bool,
@@ -612,14 +555,14 @@ impl Editor {
         }
     }
 
-    fn select(&mut self, arg: &SelectAction, cx: &mut ViewContext<Self>) {
-        match arg {
-            SelectAction::Begin { position, add } => self.begin_selection(*position, *add, cx),
-            SelectAction::Update {
+    fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext<Self>) {
+        match phase {
+            SelectPhase::Begin { position, add } => self.begin_selection(*position, *add, cx),
+            SelectPhase::Update {
                 position,
                 scroll_position,
             } => self.update_selection(*position, *scroll_position, cx),
-            SelectAction::End => self.end_selection(cx),
+            SelectPhase::End => self.end_selection(cx),
         }
     }
 

zed/src/editor/element.rs 🔗

@@ -1,4 +1,4 @@
-use super::{DisplayPoint, Editor, SelectAction, Snapshot};
+use super::{DisplayPoint, Editor, SelectPhase, Snapshot};
 use crate::time::ReplicaId;
 use gpui::{
     color::Color,
@@ -55,7 +55,7 @@ impl EditorElement {
         if paint.text_bounds.contains_point(position) {
             let snapshot = self.snapshot(cx.app);
             let position = paint.point_for_position(&snapshot, layout, position);
-            cx.dispatch_action("buffer:select", SelectAction::Begin { position, add: cmd });
+            cx.dispatch_action("buffer:select", SelectPhase::Begin { position, add: cmd });
             true
         } else {
             false
@@ -64,7 +64,7 @@ impl EditorElement {
 
     fn mouse_up(&self, _position: Vector2F, cx: &mut EventContext) -> bool {
         if self.view(cx.app.as_ref()).is_selecting() {
-            cx.dispatch_action("buffer:select", SelectAction::End);
+            cx.dispatch_action("buffer:select", SelectPhase::End);
             true
         } else {
             false
@@ -115,7 +115,7 @@ impl EditorElement {
 
             cx.dispatch_action(
                 "buffer:select",
-                SelectAction::Update {
+                SelectPhase::Update {
                     position,
                     scroll_position: (snapshot.scroll_position() + scroll_delta).clamp(
                         Vector2F::zero(),