From 3627ff87f008776ac1b63a42b0f4023374ce452f Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 4 Dec 2023 15:53:38 -0500 Subject: [PATCH 1/5] Ensure the candidate keybinding matches the correct context --- crates/gpui2/src/key_dispatch.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/gpui2/src/key_dispatch.rs b/crates/gpui2/src/key_dispatch.rs index 5fbf83bfbab8e45320ee856fd7542298abdc9649..1ab99ec487854b0081a960c5554635bcef58d014 100644 --- a/crates/gpui2/src/key_dispatch.rs +++ b/crates/gpui2/src/key_dispatch.rs @@ -167,7 +167,10 @@ impl DispatchTree { self.keymap .lock() .bindings_for_action(action.type_id()) - .filter(|candidate| candidate.action.partial_eq(action)) + .filter(|candidate| { + candidate.action.partial_eq(action) + && candidate.matches_context(&self.context_stack) + }) .cloned() .collect() } From 2c2e5144c9c034b3e15c480cab8711345671d23f Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 4 Dec 2023 21:28:37 +0000 Subject: [PATCH 2/5] Fix context key matching * You need to check all layers of the context stack * When in command, the context should be based on where focus was (to match `available_actions`. --- .../command_palette2/src/command_palette.rs | 6 ++++- crates/gpui2/src/key_dispatch.rs | 19 +++++++++++--- crates/gpui2/src/window.rs | 26 ++++++++++++++++--- crates/ui2/src/components/keybinding.rs | 15 ++++++++--- 4 files changed, 54 insertions(+), 12 deletions(-) diff --git a/crates/command_palette2/src/command_palette.rs b/crates/command_palette2/src/command_palette.rs index 04688b05492c8c298c33d32423cdb5e7ce1fe393..a2abadd5fdea652e7d3d8fda933dac549fde8ef2 100644 --- a/crates/command_palette2/src/command_palette.rs +++ b/crates/command_palette2/src/command_palette.rs @@ -311,7 +311,11 @@ impl PickerDelegate for CommandPaletteDelegate { command.name.clone(), r#match.positions.clone(), )) - .children(KeyBinding::for_action(&*command.action, cx)), + .children(KeyBinding::for_action_in( + &*command.action, + &self.previous_focus_handle, + cx, + )), ), ) } diff --git a/crates/gpui2/src/key_dispatch.rs b/crates/gpui2/src/key_dispatch.rs index 1ab99ec487854b0081a960c5554635bcef58d014..95915b98ed3dedfb496b8bc85bb96b86682666ff 100644 --- a/crates/gpui2/src/key_dispatch.rs +++ b/crates/gpui2/src/key_dispatch.rs @@ -16,7 +16,7 @@ pub struct DispatchNodeId(usize); pub(crate) struct DispatchTree { node_stack: Vec, - context_stack: Vec, + pub(crate) context_stack: Vec, nodes: Vec, focusable_node_ids: HashMap, keystroke_matchers: HashMap, KeystrokeMatcher>, @@ -163,13 +163,24 @@ impl DispatchTree { actions } - pub fn bindings_for_action(&self, action: &dyn Action) -> Vec { + pub fn bindings_for_action( + &self, + action: &dyn Action, + context_stack: &Vec, + ) -> Vec { self.keymap .lock() .bindings_for_action(action.type_id()) .filter(|candidate| { - candidate.action.partial_eq(action) - && candidate.matches_context(&self.context_stack) + if !candidate.action.partial_eq(action) { + return false; + } + for i in 1..context_stack.len() { + if candidate.matches_context(&context_stack[0..i]) { + return true; + } + } + return false; }) .cloned() .collect() diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 5724f1e0701a2b960afb478fad0186649c29debd..b88e89ef55d6d41221491acaa70e0f83f5a98190 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -1492,10 +1492,28 @@ impl<'a> WindowContext<'a> { } pub fn bindings_for_action(&self, action: &dyn Action) -> Vec { - self.window - .current_frame - .dispatch_tree - .bindings_for_action(action) + self.window.current_frame.dispatch_tree.bindings_for_action( + action, + &self.window.current_frame.dispatch_tree.context_stack, + ) + } + + pub fn bindings_for_action_in( + &self, + action: &dyn Action, + focus_handle: &FocusHandle, + ) -> Vec { + let dispatch_tree = &self.window.previous_frame.dispatch_tree; + + let Some(node_id) = dispatch_tree.focusable_node_id(focus_handle.id) else { + return vec![]; + }; + let context_stack = dispatch_tree + .dispatch_path(node_id) + .into_iter() + .map(|node_id| dispatch_tree.node(node_id).context.clone()) + .collect(); + dispatch_tree.bindings_for_action(action, &context_stack) } pub fn listener_for( diff --git a/crates/ui2/src/components/keybinding.rs b/crates/ui2/src/components/keybinding.rs index 993e2f323e7d2305cf4963f1fbd780a77441f208..c4054fa1a434e677c3480740c31ee55ec45cb419 100644 --- a/crates/ui2/src/components/keybinding.rs +++ b/crates/ui2/src/components/keybinding.rs @@ -1,5 +1,5 @@ use crate::{h_stack, prelude::*, Icon, IconElement, IconSize}; -use gpui::{relative, rems, Action, Div, IntoElement, Keystroke}; +use gpui::{relative, rems, Action, Div, FocusHandle, IntoElement, Keystroke}; #[derive(IntoElement, Clone)] pub struct KeyBinding { @@ -49,12 +49,21 @@ impl RenderOnce for KeyBinding { impl KeyBinding { pub fn for_action(action: &dyn Action, cx: &mut WindowContext) -> Option { - // todo! this last is arbitrary, we want to prefer users key bindings over defaults, - // and vim over normal (in vim mode), etc. let key_binding = cx.bindings_for_action(action).last().cloned()?; Some(Self::new(key_binding)) } + // like for_action(), but lets you specify the context from which keybindings + // are matched. + pub fn for_action_in( + action: &dyn Action, + focus: &FocusHandle, + cx: &mut WindowContext, + ) -> Option { + let key_binding = cx.bindings_for_action_in(action, focus).last().cloned()?; + Some(Self::new(key_binding)) + } + fn icon_for_key(keystroke: &Keystroke) -> Option { let mut icon: Option = None; From 79773178c836bce90ee839d97f9bd5f88d9eea96 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 4 Dec 2023 21:37:47 +0000 Subject: [PATCH 3/5] I was soooo close --- crates/gpui2/src/key_dispatch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui2/src/key_dispatch.rs b/crates/gpui2/src/key_dispatch.rs index 95915b98ed3dedfb496b8bc85bb96b86682666ff..4838b1a612ce65ba33c03ac25da878a752f716d3 100644 --- a/crates/gpui2/src/key_dispatch.rs +++ b/crates/gpui2/src/key_dispatch.rs @@ -176,7 +176,7 @@ impl DispatchTree { return false; } for i in 1..context_stack.len() { - if candidate.matches_context(&context_stack[0..i]) { + if candidate.matches_context(&context_stack[0..=i]) { return true; } } From c82fea375dd383eba4299a4c6297fc947e07555d Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 4 Dec 2023 22:54:48 +0000 Subject: [PATCH 4/5] Dispatch actions on focused node Allows us to implement context menu matching nicely --- crates/editor2/src/editor.rs | 1 + crates/gpui2/src/elements/div.rs | 31 ++++++----- crates/gpui2/src/window.rs | 17 ++++-- crates/ui2/src/components/context_menu.rs | 67 +++++++++++++++++++---- 4 files changed, 85 insertions(+), 31 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 6b223243996e32946de9cdcdd15b3eb68e989d43..dfad00036e612bd79f6c6a9b682de10038725b20 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -3640,6 +3640,7 @@ impl Editor { } pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext) { + dbg!("TOGGLE CODE ACTIONS"); let mut context_menu = self.context_menu.write(); if matches!(context_menu.as_ref(), Some(ContextMenu::CodeActions(_))) { *context_menu = None; diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index 68dca4c9d144a571d894f63f382c2d9489f7251c..2551ddf4a840becff071d1eefa7b3d55c20694ee 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -221,20 +221,6 @@ pub trait InteractiveElement: Sized + Element { /// Add a listener for the given action, fires during the bubble event phase fn on_action(mut self, listener: impl Fn(&A, &mut WindowContext) + 'static) -> Self { - // NOTE: this debug assert has the side-effect of working around - // a bug where a crate consisting only of action definitions does - // not register the actions in debug builds: - // - // https://github.com/rust-lang/rust/issues/47384 - // https://github.com/mmastrac/rust-ctor/issues/280 - // - // if we are relying on this side-effect still, removing the debug_assert! - // likely breaks the command_palette tests. - // debug_assert!( - // A::is_registered(), - // "{:?} is not registered as an action", - // A::qualified_name() - // ); self.interactivity().action_listeners.push(( TypeId::of::(), Box::new(move |action, phase, cx| { @@ -247,6 +233,23 @@ pub trait InteractiveElement: Sized + Element { self } + fn on_boxed_action( + mut self, + action: &Box, + listener: impl Fn(&Box, &mut WindowContext) + 'static, + ) -> Self { + let action = action.boxed_clone(); + self.interactivity().action_listeners.push(( + (*action).type_id(), + Box::new(move |_, phase, cx| { + if phase == DispatchPhase::Bubble { + (listener)(&action, cx) + } + }), + )); + self + } + fn on_key_down( mut self, listener: impl Fn(&KeyDownEvent, &mut WindowContext) + 'static, diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index b88e89ef55d6d41221491acaa70e0f83f5a98190..09bc2c561836f399ca4325b563494bda13cdd00c 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -1348,6 +1348,8 @@ impl<'a> WindowContext<'a> { .dispatch_tree .dispatch_path(node_id); + let mut actions: Vec> = Vec::new(); + // Capture phase let mut context_stack: SmallVec<[KeyContext; 16]> = SmallVec::new(); self.propagate_event = true; @@ -1382,22 +1384,26 @@ impl<'a> WindowContext<'a> { let node = self.window.current_frame.dispatch_tree.node(*node_id); if !node.context.is_empty() { if let Some(key_down_event) = event.downcast_ref::() { - if let Some(action) = self + if let Some(found) = self .window .current_frame .dispatch_tree .dispatch_key(&key_down_event.keystroke, &context_stack) { - self.dispatch_action_on_node(*node_id, action); - if !self.propagate_event { - return; - } + actions.push(found.boxed_clone()) } } context_stack.pop(); } } + + for action in actions { + self.dispatch_action_on_node(node_id, action); + if !self.propagate_event { + return; + } + } } } @@ -1425,7 +1431,6 @@ impl<'a> WindowContext<'a> { } } } - // Bubble phase for node_id in dispatch_path.iter().rev() { let node = self.window.current_frame.dispatch_tree.node(*node_id); diff --git a/crates/ui2/src/components/context_menu.rs b/crates/ui2/src/components/context_menu.rs index 54c8d9337574c914f3545ae9066279eeb6027936..9a5390a8d5e93c177822b7b0be2d80d35d0f5189 100644 --- a/crates/ui2/src/components/context_menu.rs +++ b/crates/ui2/src/components/context_menu.rs @@ -7,7 +7,7 @@ use gpui::{ IntoElement, Render, View, VisualContext, }; use menu::{SelectFirst, SelectLast, SelectNext, SelectPrev}; -use std::rc::Rc; +use std::{rc::Rc, time::Duration}; pub enum ContextMenuItem { Separator, @@ -16,7 +16,7 @@ pub enum ContextMenuItem { label: SharedString, icon: Option, handler: Rc, - key_binding: Option, + action: Option>, }, } @@ -70,8 +70,8 @@ impl ContextMenu { self.items.push(ContextMenuItem::Entry { label: label.into(), handler: Rc::new(on_click), - key_binding: None, icon: None, + action: None, }); self } @@ -84,7 +84,7 @@ impl ContextMenu { ) -> Self { self.items.push(ContextMenuItem::Entry { label: label.into(), - key_binding: KeyBinding::for_action(&*action, cx), + action: Some(action.boxed_clone()), handler: Rc::new(move |cx| cx.dispatch_action(action.boxed_clone())), icon: None, }); @@ -99,7 +99,7 @@ impl ContextMenu { ) -> Self { self.items.push(ContextMenuItem::Entry { label: label.into(), - key_binding: KeyBinding::for_action(&*action, cx), + action: Some(action.boxed_clone()), handler: Rc::new(move |cx| cx.dispatch_action(action.boxed_clone())), icon: Some(Icon::Link), }); @@ -161,6 +161,36 @@ impl ContextMenu { self.select_last(&Default::default(), cx); } } + + pub fn on_action_dispatch(&mut self, dispatched: &Box, cx: &mut ViewContext) { + if let Some(ix) = self.items.iter().position(|item| { + if let ContextMenuItem::Entry { + action: Some(action), + .. + } = item + { + action.partial_eq(&**dispatched) + } else { + false + } + }) { + self.selected_index = Some(ix); + cx.notify(); + let action = dispatched.boxed_clone(); + cx.spawn(|this, mut cx| async move { + cx.background_executor() + .timer(Duration::from_millis(50)) + .await; + this.update(&mut cx, |this, cx| { + cx.dispatch_action(action); + this.cancel(&Default::default(), cx) + }) + }) + .detach_and_log_err(cx); + } else { + cx.propagate() + } + } } impl ContextMenuItem { @@ -185,6 +215,22 @@ impl Render for ContextMenu { .on_action(cx.listener(ContextMenu::select_prev)) .on_action(cx.listener(ContextMenu::confirm)) .on_action(cx.listener(ContextMenu::cancel)) + .map(|mut el| { + for item in self.items.iter() { + if let ContextMenuItem::Entry { + action: Some(action), + .. + } = item + { + el = el.on_boxed_action( + action, + cx.listener(ContextMenu::on_action_dispatch), + ); + } + } + el + }) + .on_blur(cx.listener(|this, _, cx| this.cancel(&Default::default(), cx))) .flex_none() .child( List::new().children(self.items.iter().enumerate().map( @@ -196,8 +242,8 @@ impl Render for ContextMenu { ContextMenuItem::Entry { label, handler, - key_binding, icon, + action, } => { let handler = handler.clone(); let dismiss = cx.listener(|_, _, cx| cx.emit(DismissEvent)); @@ -218,11 +264,10 @@ impl Render for ContextMenu { .w_full() .justify_between() .child(label_element) - .children( - key_binding - .clone() - .map(|binding| div().ml_1().child(binding)), - ), + .children(action.as_ref().and_then(|action| { + KeyBinding::for_action(&**action, cx) + .map(|binding| div().ml_1().child(binding)) + })), ) .selected(Some(ix) == self.selected_index) .on_click(move |event, cx| { From 1c9b984738aededd18390e5f015ace63e36e1b67 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 4 Dec 2023 23:35:31 +0000 Subject: [PATCH 5/5] Remove cx param --- crates/copilot_button2/src/copilot_button.rs | 3 +- crates/editor2/src/editor.rs | 1 - crates/editor2/src/mouse_context_menu.rs | 11 +++---- crates/project_panel2/src/project_panel.rs | 25 +++++++------- crates/terminal_view2/src/terminal_view.rs | 7 ++-- crates/ui2/src/components/context_menu.rs | 14 ++------ crates/workspace2/src/pane.rs | 34 ++++++-------------- 7 files changed, 31 insertions(+), 64 deletions(-) diff --git a/crates/copilot_button2/src/copilot_button.rs b/crates/copilot_button2/src/copilot_button.rs index aab59a9cad500a97a93f6c12990ba43cdb25b528..dc6f8085339de18f40bd1c4262bc3b9e0d8c9a67 100644 --- a/crates/copilot_button2/src/copilot_button.rs +++ b/crates/copilot_button2/src/copilot_button.rs @@ -201,9 +201,8 @@ impl CopilotButton { url: COPILOT_SETTINGS_URL.to_string(), } .boxed_clone(), - cx, ) - .action("Sign Out", SignOut.boxed_clone(), cx) + .action("Sign Out", SignOut.boxed_clone()) }); } diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index dfad00036e612bd79f6c6a9b682de10038725b20..6b223243996e32946de9cdcdd15b3eb68e989d43 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -3640,7 +3640,6 @@ impl Editor { } pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext) { - dbg!("TOGGLE CODE ACTIONS"); let mut context_menu = self.context_menu.write(); if matches!(context_menu.as_ref(), Some(ContextMenu::CodeActions(_))) { *context_menu = None; diff --git a/crates/editor2/src/mouse_context_menu.rs b/crates/editor2/src/mouse_context_menu.rs index fdeec9110b97ff5c23b945bf31da590bbe8a30dc..8b998ccb3a52d59bd1fb10ff5294085e6b27cfc0 100644 --- a/crates/editor2/src/mouse_context_menu.rs +++ b/crates/editor2/src/mouse_context_menu.rs @@ -37,19 +37,18 @@ pub fn deploy_context_menu( }); let context_menu = ui::ContextMenu::build(cx, |menu, cx| { - menu.action("Rename Symbol", Box::new(Rename), cx) - .action("Go to Definition", Box::new(GoToDefinition), cx) - .action("Go to Type Definition", Box::new(GoToTypeDefinition), cx) - .action("Find All References", Box::new(FindAllReferences), cx) + menu.action("Rename Symbol", Box::new(Rename)) + .action("Go to Definition", Box::new(GoToDefinition)) + .action("Go to Type Definition", Box::new(GoToTypeDefinition)) + .action("Find All References", Box::new(FindAllReferences)) .action( "Code Actions", Box::new(ToggleCodeActions { deployed_from_indicator: false, }), - cx, ) .separator() - .action("Reveal in Finder", Box::new(RevealInFinder), cx) + .action("Reveal in Finder", Box::new(RevealInFinder)) }); let context_menu_focus = context_menu.focus_handle(cx); cx.focus(&context_menu_focus); diff --git a/crates/project_panel2/src/project_panel.rs b/crates/project_panel2/src/project_panel.rs index 0a5a63f14a289f5f8aeb44a20a454a35f8bea20b..ce039071cf830d3282dcc860fc6ce083576aafc1 100644 --- a/crates/project_panel2/src/project_panel.rs +++ b/crates/project_panel2/src/project_panel.rs @@ -397,7 +397,6 @@ impl ProjectPanel { menu = menu.action( "Add Folder to Project", Box::new(workspace::AddFolderToProject), - cx, ); if is_root { menu = menu.entry( @@ -412,35 +411,35 @@ impl ProjectPanel { } menu = menu - .action("New File", Box::new(NewFile), cx) - .action("New Folder", Box::new(NewDirectory), cx) + .action("New File", Box::new(NewFile)) + .action("New Folder", Box::new(NewDirectory)) .separator() - .action("Cut", Box::new(Cut), cx) - .action("Copy", Box::new(Copy), cx); + .action("Cut", Box::new(Cut)) + .action("Copy", Box::new(Copy)); if let Some(clipboard_entry) = self.clipboard_entry { if clipboard_entry.worktree_id() == worktree_id { - menu = menu.action("Paste", Box::new(Paste), cx); + menu = menu.action("Paste", Box::new(Paste)); } } menu = menu .separator() - .action("Copy Path", Box::new(CopyPath), cx) - .action("Copy Relative Path", Box::new(CopyRelativePath), cx) + .action("Copy Path", Box::new(CopyPath)) + .action("Copy Relative Path", Box::new(CopyRelativePath)) .separator() - .action("Reveal in Finder", Box::new(RevealInFinder), cx); + .action("Reveal in Finder", Box::new(RevealInFinder)); if is_dir { menu = menu - .action("Open in Terminal", Box::new(OpenInTerminal), cx) - .action("Search Inside", Box::new(NewSearchInDirectory), cx) + .action("Open in Terminal", Box::new(OpenInTerminal)) + .action("Search Inside", Box::new(NewSearchInDirectory)) } - menu = menu.separator().action("Rename", Box::new(Rename), cx); + menu = menu.separator().action("Rename", Box::new(Rename)); if !is_root { - menu = menu.action("Delete", Box::new(Delete), cx); + menu = menu.action("Delete", Box::new(Delete)); } menu diff --git a/crates/terminal_view2/src/terminal_view.rs b/crates/terminal_view2/src/terminal_view.rs index b007d58c34bcb2163f42bd2b88e1979a18152f56..4186a610bf0ae11cdb4bef306e2df2ef1b9b7925 100644 --- a/crates/terminal_view2/src/terminal_view.rs +++ b/crates/terminal_view2/src/terminal_view.rs @@ -299,11 +299,8 @@ impl TerminalView { cx: &mut ViewContext, ) { self.context_menu = Some(ContextMenu::build(cx, |menu, cx| { - menu.action("Clear", Box::new(Clear), cx).action( - "Close", - Box::new(CloseActiveItem { save_intent: None }), - cx, - ) + menu.action("Clear", Box::new(Clear)) + .action("Close", Box::new(CloseActiveItem { save_intent: None })) })); dbg!(&position); // todo!() diff --git a/crates/ui2/src/components/context_menu.rs b/crates/ui2/src/components/context_menu.rs index 9a5390a8d5e93c177822b7b0be2d80d35d0f5189..27aa73b4fe35d38457301fad78a15cb4b0986b23 100644 --- a/crates/ui2/src/components/context_menu.rs +++ b/crates/ui2/src/components/context_menu.rs @@ -76,12 +76,7 @@ impl ContextMenu { self } - pub fn action( - mut self, - label: impl Into, - action: Box, - cx: &mut WindowContext, - ) -> Self { + pub fn action(mut self, label: impl Into, action: Box) -> Self { self.items.push(ContextMenuItem::Entry { label: label.into(), action: Some(action.boxed_clone()), @@ -91,12 +86,7 @@ impl ContextMenu { self } - pub fn link( - mut self, - label: impl Into, - action: Box, - cx: &mut WindowContext, - ) -> Self { + pub fn link(mut self, label: impl Into, action: Box) -> Self { self.items.push(ContextMenuItem::Entry { label: label.into(), action: Some(action.boxed_clone()), diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index 438ad396936e740fe0c1cc3518e7bdca9f02e941..855ce0e931e4c13b78d6312df1b1ad79fb4bd433 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -1531,24 +1531,17 @@ impl Pane { menu.action( "Close Active Item", CloseActiveItem { save_intent: None }.boxed_clone(), - cx, - ) - .action("Close Inactive Items", CloseInactiveItems.boxed_clone(), cx) - .action("Close Clean Items", CloseCleanItems.boxed_clone(), cx) - .action( - "Close Items To The Left", - CloseItemsToTheLeft.boxed_clone(), - cx, ) + .action("Close Inactive Items", CloseInactiveItems.boxed_clone()) + .action("Close Clean Items", CloseCleanItems.boxed_clone()) + .action("Close Items To The Left", CloseItemsToTheLeft.boxed_clone()) .action( "Close Items To The Right", CloseItemsToTheRight.boxed_clone(), - cx, ) .action( "Close All Items", CloseAllItems { save_intent: None }.boxed_clone(), - cx, ) }) }) @@ -1627,17 +1620,12 @@ impl Pane { .child(IconButton::new("plus", Icon::Plus).on_click( cx.listener(|this, _, cx| { let menu = ContextMenu::build(cx, |menu, cx| { - menu.action("New File", NewFile.boxed_clone(), cx) + menu.action("New File", NewFile.boxed_clone()) .action( "New Terminal", NewCenterTerminal.boxed_clone(), - cx, - ) - .action( - "New Search", - NewSearch.boxed_clone(), - cx, ) + .action("New Search", NewSearch.boxed_clone()) }); cx.subscribe( &menu, @@ -1661,14 +1649,10 @@ impl Pane { .child(IconButton::new("split", Icon::Split).on_click( cx.listener(|this, _, cx| { let menu = ContextMenu::build(cx, |menu, cx| { - menu.action( - "Split Right", - SplitRight.boxed_clone(), - cx, - ) - .action("Split Left", SplitLeft.boxed_clone(), cx) - .action("Split Up", SplitUp.boxed_clone(), cx) - .action("Split Down", SplitDown.boxed_clone(), cx) + menu.action("Split Right", SplitRight.boxed_clone()) + .action("Split Left", SplitLeft.boxed_clone()) + .action("Split Up", SplitUp.boxed_clone()) + .action("Split Down", SplitDown.boxed_clone()) }); cx.subscribe( &menu,