editor: Hide hover links when mouse cursor is not visible (#50424)

When I am in the middle of editing, pressing Ctrl would
counter-intuitively highlight links even when the mouse cursor is
hidden. This change considers the state of the mouse cursor before
painting links on hover.

Before: Modifier pressed, cursor hidden, link visible

<img width="506" height="518" alt="image"
src="https://github.com/user-attachments/assets/82a59e83-e3cb-490f-b292-148686ec569d"
/>

After: Modifier pressed, cursor hidden (red dot indicates current cursor
position)

<img width="408" height="298" alt="image"
src="https://github.com/user-attachments/assets/c19ed83c-4778-4890-97b9-5155cdcf658b"
/>

Before you mark this PR as ready for review, make sure that you have:
- [x] Added a solid test coverage and/or screenshots from doing manual
testing
- [x] Done a self-review taking into account security and performance
aspects
- [x] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)

Release Notes:

- Fixed spurious link highlighting when mouse cursor is hidden

Fixes #50776
This commit is contained in:
Karthik Nishanth 2026-03-05 23:53:24 -08:00 committed by GitHub
parent a70c295658
commit e9ffef0bfa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -119,7 +119,7 @@ impl Editor {
cx: &mut Context<Self>,
) {
let hovered_link_modifier = Editor::is_cmd_or_ctrl_pressed(&modifiers, cx);
if !hovered_link_modifier || self.has_pending_selection() {
if !hovered_link_modifier || self.has_pending_selection() || self.mouse_cursor_hidden {
self.hide_hovered_link(cx);
return;
}
@ -782,7 +782,7 @@ fn surrounding_filename(
mod tests {
use super::*;
use crate::{
DisplayPoint,
DisplayPoint, HideMouseCursorOrigin,
display_map::ToDisplayPoint,
editor_tests::init_test,
inlays::inlay_hints::tests::{cached_hint_labels, visible_hint_labels},
@ -1362,6 +1362,82 @@ mod tests {
);
}
#[gpui::test]
async fn test_hover_preconditions(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
..Default::default()
},
cx,
)
.await;
macro_rules! assert_no_highlight {
($cx:expr) => {
// No highlight
$cx.update_editor(|editor, window, cx| {
assert!(
editor
.snapshot(window, cx)
.text_highlight_ranges(HighlightKey::HoveredLinkState)
.unwrap_or_default()
.1
.is_empty()
);
});
};
}
// No link
cx.set_state(indoc! {"
Let's test a [complex](https://zed.dev/channel/) caseˇ.
"});
assert_no_highlight!(cx);
// No modifier
let screen_coord = cx.pixel_position(indoc! {"
Let's test a [complex](https://zed.dev/channel/ˇ) case.
"});
cx.simulate_mouse_move(screen_coord, None, Modifiers::none());
assert_no_highlight!(cx);
// Modifier active
let screen_coord = cx.pixel_position(indoc! {"
Let's test a [complex](https://zed.dev/channeˇl/) case.
"});
cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key());
cx.assert_editor_text_highlights(
HighlightKey::HoveredLinkState,
indoc! {"
Let's test a [complex](«https://zed.dev/channel/ˇ») case.
"},
);
// Cursor hidden with secondary key
let screen_coord = cx.pixel_position(indoc! {"
Let's test a [complex](https://zed.dev/ˇchannel/) case.
"});
cx.simulate_mouse_move(screen_coord, None, Modifiers::none());
cx.update_editor(|editor, _, cx| {
editor.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
});
cx.simulate_modifiers_change(Modifiers::secondary_key());
assert_no_highlight!(cx);
// Cursor active again
let screen_coord = cx.pixel_position(indoc! {"
Let's test a [complex](https://ˇzed.dev/channel/) case.
"});
cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key());
cx.assert_editor_text_highlights(
HighlightKey::HoveredLinkState,
indoc! {"
Let's test a [complex](«https://zed.dev/channel/ˇ») case.
"},
);
}
#[gpui::test]
async fn test_urls_at_beginning_of_buffer(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});