Merge pull request #1114 from zed-industries/fix-menu-bindings

Antonio Scandurra created

Add keystroke for menu item only when action is equal to binding

Change summary

crates/client/src/user.rs                         |  2 
crates/contacts_panel/src/contact_notification.rs |  4 +-
crates/contacts_panel/src/contacts_panel.rs       |  8 +++---
crates/diagnostics/src/diagnostics.rs             |  2 
crates/editor/src/editor.rs                       | 22 ++++++++--------
crates/gpui/src/app.rs                            |  4 +-
crates/gpui/src/app/action.rs                     |  9 ++++++
crates/gpui/src/keymap.rs                         |  6 +++
crates/gpui/src/platform/mac/platform.rs          |  2 
crates/gpui/src/views/select.rs                   |  2 
crates/menu/src/menu.rs                           |  2 
crates/project_panel/src/project_panel.rs         |  6 ++--
crates/search/src/buffer_search.rs                |  4 +-
crates/search/src/search.rs                       |  4 +-
crates/vim/src/motion.rs                          |  6 ++--
crates/vim/src/normal/change.rs                   |  2 
crates/vim/src/vim.rs                             |  4 +-
crates/workspace/src/pane.rs                      | 10 +++---
crates/workspace/src/pane_group.rs                |  2 
crates/workspace/src/sidebar.rs                   |  6 ++--
crates/workspace/src/workspace.rs                 | 10 +++---
crates/zed/src/zed.rs                             |  2 
22 files changed, 66 insertions(+), 53 deletions(-)

Detailed changes

crates/client/src/user.rs 🔗

@@ -35,7 +35,7 @@ impl PartialEq for User {
 
 impl Eq for User {}
 
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
 pub struct Contact {
     pub user: Arc<User>,
     pub online: bool,

crates/contacts_panel/src/contact_notification.rs 🔗

@@ -21,10 +21,10 @@ pub struct ContactNotification {
     kind: client::ContactEventKind,
 }
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 struct Dismiss(u64);
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct RespondToContactRequest {
     pub user_id: u64,
     pub accept: bool,

crates/contacts_panel/src/contacts_panel.rs 🔗

@@ -48,7 +48,7 @@ enum ContactEntry {
     OfflineProject(WeakModelHandle<Project>),
 }
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 struct ToggleExpanded(Section);
 
 pub struct ContactsPanel {
@@ -63,13 +63,13 @@ pub struct ContactsPanel {
     _maintain_contacts: Subscription,
 }
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct RequestContact(pub u64);
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct RemoveContact(pub u64);
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct RespondToContactRequest {
     pub user_id: u64,
     pub accept: bool,

crates/diagnostics/src/diagnostics.rs 🔗

@@ -60,7 +60,7 @@ struct PathState {
     diagnostic_groups: Vec<DiagnosticGroupState>,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq)]
 struct Jump {
     path: ProjectPath,
     position: Point,

crates/editor/src/editor.rs 🔗

@@ -65,49 +65,49 @@ const MAX_LINE_LEN: usize = 1024;
 const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
 const MAX_SELECTION_HISTORY_LEN: usize = 1024;
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct SelectNext {
     #[serde(default)]
     pub replace_newest: bool,
 }
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct GoToDiagnostic(pub Direction);
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct Scroll(pub Vector2F);
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct Select(pub SelectPhase);
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct Input(pub String);
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct SelectToBeginningOfLine {
     #[serde(default)]
     stop_at_soft_wraps: bool,
 }
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct SelectToEndOfLine {
     #[serde(default)]
     stop_at_soft_wraps: bool,
 }
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct ToggleCodeActions {
     #[serde(default)]
     pub deployed_from_indicator: bool,
 }
 
-#[derive(Clone, Default, Deserialize)]
+#[derive(Clone, Default, Deserialize, PartialEq)]
 pub struct ConfirmCompletion {
     #[serde(default)]
     pub item_ix: Option<usize>,
 }
 
-#[derive(Clone, Default, Deserialize)]
+#[derive(Clone, Default, Deserialize, PartialEq)]
 pub struct ConfirmCodeAction {
     #[serde(default)]
     pub item_ix: Option<usize>,
@@ -307,7 +307,7 @@ trait InvalidationRegion {
     fn ranges(&self) -> &[Range<Anchor>];
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq)]
 pub enum SelectPhase {
     Begin {
         position: DisplayPoint,

crates/gpui/src/app.rs 🔗

@@ -6336,7 +6336,7 @@ mod tests {
             }
         }
 
-        #[derive(Clone, Default, Deserialize)]
+        #[derive(Clone, Default, Deserialize, PartialEq)]
         pub struct Action(pub String);
 
         impl_actions!(test, [Action]);
@@ -6459,7 +6459,7 @@ mod tests {
 
     #[crate::test(self)]
     fn test_dispatch_keystroke(cx: &mut MutableAppContext) {
-        #[derive(Clone, Deserialize)]
+        #[derive(Clone, Deserialize, PartialEq)]
         pub struct Action(String);
 
         impl_actions!(test, [Action]);

crates/gpui/src/app/action.rs 🔗

@@ -5,6 +5,7 @@ pub trait Action: 'static {
     fn name(&self) -> &'static str;
     fn as_any(&self) -> &dyn Any;
     fn boxed_clone(&self) -> Box<dyn Action>;
+    fn eq(&self, other: &dyn Action) -> bool;
 
     fn qualified_name() -> &'static str
     where
@@ -103,6 +104,14 @@ macro_rules! __impl_action {
                 Box::new(self.clone())
             }
 
+            fn eq(&self, other: &dyn $crate::Action) -> bool {
+                if let Some(other) = other.as_any().downcast_ref::<Self>() {
+                    self == other
+                } else {
+                    false
+                }
+            }
+
             $from_json_fn
         }
     };

crates/gpui/src/keymap.rs 🔗

@@ -176,7 +176,7 @@ impl Matcher {
         cx: &Context,
     ) -> Option<SmallVec<[Keystroke; 2]>> {
         for binding in self.keymap.bindings.iter().rev() {
-            if binding.action.id() == action.id()
+            if binding.action.eq(action)
                 && binding
                     .context_predicate
                     .as_ref()
@@ -265,6 +265,10 @@ impl Binding {
     pub fn keystrokes(&self) -> &[Keystroke] {
         &self.keystrokes
     }
+
+    pub fn action(&self) -> &dyn Action {
+        self.action.as_ref()
+    }
 }
 
 impl Keystroke {

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

@@ -154,7 +154,7 @@ impl MacForegroundPlatform {
                         let mut keystroke = None;
                         if let Some(binding) = keystroke_matcher
                             .bindings_for_action_type(action.as_any().type_id())
-                            .next()
+                            .find(|binding| binding.action().eq(action.as_ref()))
                         {
                             if binding.keystrokes().len() == 1 {
                                 keystroke = binding.keystrokes().first()

crates/gpui/src/views/select.rs 🔗

@@ -27,7 +27,7 @@ pub enum ItemType {
     Unselected,
 }
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct SelectItem(pub usize);
 
 actions!(select, [ToggleSelect]);

crates/menu/src/menu.rs 🔗

@@ -1,4 +1,4 @@
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct SelectIndex(pub usize);
 
 gpui::actions!(

crates/project_panel/src/project_panel.rs 🔗

@@ -81,16 +81,16 @@ struct EntryDetails {
     is_cut: bool,
 }
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct ToggleExpanded(pub ProjectEntryId);
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct Open {
     pub entry_id: ProjectEntryId,
     pub change_focus: bool,
 }
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct DeployContextMenu {
     pub position: Vector2F,
     pub entry_id: Option<ProjectEntryId>,

crates/search/src/buffer_search.rs 🔗

@@ -16,12 +16,12 @@ use settings::Settings;
 use std::ops::Range;
 use workspace::{ItemHandle, Pane, ToolbarItemLocation, ToolbarItemView};
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct Deploy {
     pub focus: bool,
 }
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct ToggleSearchOption(pub SearchOption);
 
 actions!(buffer_search, [Dismiss, FocusEditor]);

crates/search/src/search.rs 🔗

@@ -15,13 +15,13 @@ pub fn init(cx: &mut MutableAppContext) {
     project_search::init(cx);
 }
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct ToggleSearchOption(pub SearchOption);
 
 actions!(search, [SelectNextMatch, SelectPrevMatch]);
 impl_internal_actions!(search, [ToggleSearchOption]);
 
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, PartialEq)]
 pub enum SearchOption {
     WholeWord,
     CaseSensitive,

crates/vim/src/motion.rs 🔗

@@ -32,21 +32,21 @@ pub enum Motion {
     EndOfDocument,
 }
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 #[serde(rename_all = "camelCase")]
 struct NextWordStart {
     #[serde(default)]
     ignore_punctuation: bool,
 }
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 #[serde(rename_all = "camelCase")]
 struct NextWordEnd {
     #[serde(default)]
     ignore_punctuation: bool,
 }
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 #[serde(rename_all = "camelCase")]
 struct PreviousWordStart {
     #[serde(default)]

crates/vim/src/normal/change.rs 🔗

@@ -4,7 +4,7 @@ use gpui::{impl_actions, MutableAppContext, ViewContext};
 use serde::Deserialize;
 use workspace::Workspace;
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 #[serde(rename_all = "camelCase")]
 struct ChangeWord {
     #[serde(default)]

crates/vim/src/vim.rs 🔗

@@ -18,10 +18,10 @@ use settings::Settings;
 use state::{Mode, Operator, VimState};
 use workspace::{self, Workspace};
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct SwitchMode(pub Mode);
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct PushOperator(pub Operator);
 
 impl_actions!(vim, [SwitchMode, PushOperator]);

crates/workspace/src/pane.rs 🔗

@@ -28,25 +28,25 @@ actions!(
     ]
 );
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct Split(pub SplitDirection);
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct CloseItem {
     pub item_id: usize,
     pub pane: WeakViewHandle<Pane>,
 }
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct ActivateItem(pub usize);
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct GoBack {
     #[serde(skip_deserializing)]
     pub pane: Option<WeakViewHandle<Pane>>,
 }
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct GoForward {
     #[serde(skip_deserializing)]
     pub pane: Option<WeakViewHandle<Pane>>,

crates/workspace/src/pane_group.rs 🔗

@@ -255,7 +255,7 @@ impl PaneAxis {
     }
 }
 
-#[derive(Clone, Copy, Debug, Deserialize)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
 pub enum SplitDirection {
     Up,
     Down,

crates/workspace/src/sidebar.rs 🔗

@@ -60,7 +60,7 @@ pub struct Sidebar {
     custom_width: Rc<RefCell<f32>>,
 }
 
-#[derive(Clone, Copy, Debug, Deserialize)]
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
 pub enum Side {
     Left,
     Right,
@@ -76,13 +76,13 @@ pub struct SidebarButtons {
     sidebar: ViewHandle<Sidebar>,
 }
 
-#[derive(Clone, Debug, Deserialize)]
+#[derive(Clone, Debug, Deserialize, PartialEq)]
 pub struct ToggleSidebarItem {
     pub side: Side,
     pub item_index: usize,
 }
 
-#[derive(Clone, Debug, Deserialize)]
+#[derive(Clone, Debug, Deserialize, PartialEq)]
 pub struct ToggleSidebarItemFocus {
     pub side: Side,
     pub item_index: usize,

crates/workspace/src/workspace.rs 🔗

@@ -73,7 +73,7 @@ type FollowableItemBuilders = HashMap<
     ),
 >;
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct RemoveFolderFromProject(pub WorktreeId);
 
 actions!(
@@ -94,21 +94,21 @@ actions!(
     ]
 );
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct OpenPaths {
     pub paths: Vec<PathBuf>,
 }
 
-#[derive(Clone, Deserialize)]
+#[derive(Clone, Deserialize, PartialEq)]
 pub struct ToggleProjectOnline {
     #[serde(skip_deserializing)]
     pub project: Option<ModelHandle<Project>>,
 }
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct ToggleFollow(pub PeerId);
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
 pub struct JoinProject {
     pub contact: Arc<Contact>,
     pub project_index: usize,

crates/zed/src/zed.rs 🔗

@@ -35,7 +35,7 @@ use util::ResultExt;
 pub use workspace;
 use workspace::{AppState, Workspace};
 
-#[derive(Deserialize, Clone)]
+#[derive(Deserialize, Clone, PartialEq)]
 struct OpenBrowser {
     url: Arc<str>,
 }