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.
This commit is contained in:
Gautam Manchandani authored and GitHub committed 2026-09-12 17:16:02 +00:00
1 parent 9d272b0363
commit a2651e3b56
2 files changed
+58 -10

No files matched your search

+17 -6
View File
@@ -1728,6 +1728,8 @@ struct GutterButtonTooltip {
primary: GutterButtonIntent,
secondary: GutterButtonIntent,
focus_handle: FocusHandle,
#[cfg(test)]
on_render: Option<Rc<RefCell<Vec<(String, String)>>>>,
}
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<Self>) -> 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()
})
+41 -4
View File
@@ -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, |_| {});