From a2651e3b5639d7f050d02ca8a863ca7d078a7dad Mon Sep 17 00:00:00 2001 From: Gautam Manchandani Date: Sat, 12 Sep 2026 17:16:02 +0000 Subject: [PATCH] editor: Fix gutter tooltip modifier hint (#64124) # Objective Fixes #58821. Follow-up to #58880: keep the modifier-click hint accurate when the tooltip switches from breakpoint to bookmark. ## Solution Build the hint from the configured secondary gutter action instead of the currently active action. ## Self-Review Checklist - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase https://github.com/user-attachments/assets/c514d30e-9747-40a1-b359-54bc10086366 --- Release Notes: - Fixed the gutter tooltip's modifier-click hint after holding Command. --- crates/editor/src/editor.rs | 23 +++++++++++----- crates/editor/src/editor_tests.rs | 45 ++++++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index f001d24bfa..8537f78f69 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1728,6 +1728,8 @@ struct GutterButtonTooltip { primary: GutterButtonIntent, secondary: GutterButtonIntent, focus_handle: FocusHandle, + #[cfg(test)] + on_render: Option>>>, } impl GutterButtonTooltip { @@ -1739,7 +1741,7 @@ impl GutterButtonTooltip { } } - fn meta_text(&self, intent: GutterButtonIntent) -> String { + fn meta_text(&self) -> String { const RIGHT_CLICK_HINT: &str = "right-click for more options"; if self.primary == self.secondary { @@ -1749,11 +1751,11 @@ impl GutterButtonTooltip { modifiers: Modifiers::secondary_key(), ..Default::default() }; - let other = match intent { - GutterButtonIntent::SetBookmark => "breakpoint", - GutterButtonIntent::SetBreakpoint => "bookmark", + let secondary = match self.secondary { + GutterButtonIntent::SetBookmark => "bookmark", + GutterButtonIntent::SetBreakpoint => "breakpoint", }; - format!("{modifier_as_text}-click to add a {other}\n{RIGHT_CLICK_HINT}") + format!("{modifier_as_text}-click to add a {secondary}\n{RIGHT_CLICK_HINT}") } } @@ -1761,7 +1763,14 @@ impl Render for GutterButtonTooltip { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { let intent = self.active_intent(window.modifiers()); let key_binding = KeyBinding::for_action_in(intent.action(), &self.focus_handle, cx); - let meta_text = self.meta_text(intent); + let meta_text = self.meta_text(); + + #[cfg(test)] + if let Some(on_render) = &self.on_render { + on_render + .borrow_mut() + .push((intent.as_str().to_owned(), meta_text.clone())); + } tooltip_container(cx, move |this, _| { this.child( @@ -4821,6 +4830,8 @@ impl Editor { primary, secondary, focus_handle: focus_handle.clone(), + #[cfg(test)] + on_render: None, }) .into() }) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index f3b165be82..c829b959d1 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -33144,6 +33144,7 @@ fn test_gutter_button_tooltip_updates_intent_with_secondary_modifier(cx: &mut Te primary: GutterButtonIntent::SetBreakpoint, secondary: GutterButtonIntent::SetBookmark, focus_handle, + on_render: None, }; let primary_intent = tooltip.active_intent(Modifiers::none()); @@ -33156,11 +33157,10 @@ fn test_gutter_button_tooltip_updates_intent_with_secondary_modifier(cx: &mut Te // When both features are enabled, the meta text advertises the // modifier-click alternative. - let meta = tooltip.meta_text(primary_intent); + let meta = tooltip.meta_text(); assert!(meta.contains("-click to add a bookmark"), "got: {meta}"); + assert!(!meta.contains("-click to add a breakpoint"), "got: {meta}"); assert!(meta.contains("right-click for more options")); - let meta = tooltip.meta_text(secondary_intent); - assert!(meta.contains("-click to add a breakpoint"), "got: {meta}"); // When only one feature is enabled (primary == secondary), a // modifier-click repeats the primary action, so the tooltip must not @@ -33169,11 +33169,48 @@ fn test_gutter_button_tooltip_updates_intent_with_secondary_modifier(cx: &mut Te primary: GutterButtonIntent::SetBreakpoint, secondary: GutterButtonIntent::SetBreakpoint, focus_handle: tooltip.focus_handle, + on_render: None, }; - let meta = single_feature_tooltip.meta_text(GutterButtonIntent::SetBreakpoint); + let meta = single_feature_tooltip.meta_text(); assert_eq!(meta, "right-click for more options"); } +#[gpui::test] +fn test_gutter_button_tooltip_renders_modifier_transitions(cx: &mut TestAppContext) { + init_test(cx, |_| {}); + + let renders = Rc::new(RefCell::new(Vec::new())); + let (tooltip, cx) = cx.add_window_view({ + let renders = renders.clone(); + move |_window, cx| GutterButtonTooltip { + primary: GutterButtonIntent::SetBreakpoint, + secondary: GutterButtonIntent::SetBookmark, + focus_handle: cx.focus_handle(), + on_render: Some(renders), + } + }); + cx.run_until_parked(); + + let assert_render = |expected_title: &str| { + let (title, meta) = renders.borrow().last().cloned().expect("tooltip rendered"); + assert_eq!(title, expected_title); + assert!(meta.contains("-click to add a bookmark"), "got: {meta}"); + assert!(!meta.contains("-click to add a breakpoint"), "got: {meta}"); + }; + + assert_render("Set Breakpoint"); + + cx.simulate_modifiers_change(Modifiers::secondary_key()); + tooltip.update_in(cx, |_, _window, cx| cx.notify()); + cx.run_until_parked(); + assert_render("Set Bookmark"); + + cx.simulate_modifiers_change(Modifiers::none()); + tooltip.update_in(cx, |_, _window, cx| cx.notify()); + cx.run_until_parked(); + assert_render("Set Breakpoint"); +} + #[gpui::test] async fn test_breakpoint_toggling(cx: &mut TestAppContext) { init_test(cx, |_| {});