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
After: Modifier pressed, cursor hidden (red dot indicates current cursor
position)
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, |_| {});