From e9ffef0bfa1ed1e668b550b0889c993fbcbe89ac Mon Sep 17 00:00:00 2001 From: Karthik Nishanth <7759435+nishanthkarthik@users.noreply.github.com> Date: Thu, 5 Mar 2026 23:53:24 -0800 Subject: [PATCH] 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 image After: Modifier pressed, cursor hidden (red dot indicates current cursor position) image 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 --- crates/editor/src/hover_links.rs | 80 +++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/crates/editor/src/hover_links.rs b/crates/editor/src/hover_links.rs index d4877a5f1986685bea37f243edf4ac8bbdfdf9f5..659a383d6b20129909b4c3f2d7bdbfbe5e580f4e 100644 --- a/crates/editor/src/hover_links.rs +++ b/crates/editor/src/hover_links.rs @@ -119,7 +119,7 @@ impl Editor { cx: &mut Context, ) { 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, |_| {});