From a61e478152fc3a8b86162fb230a66e79852121b7 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 2 Jul 2025 15:50:02 -0400 Subject: [PATCH] Reproduce #33715 in a test --- crates/editor/src/hover_popover.rs | 234 +++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index cae4789535..0ace186b8b 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -1883,4 +1883,238 @@ mod tests { ); }); } + + #[gpui::test] + async fn test_hover_on_inlay_hint_types(cx: &mut gpui::TestAppContext) { + use crate::{DisplayPoint, PointForPosition}; + use std::sync::Arc; + use std::sync::atomic::{self, AtomicUsize}; + + init_test(cx, |settings| { + settings.defaults.inlay_hints = Some(InlayHintSettings { + enabled: true, + show_type_hints: true, + show_value_hints: true, + show_parameter_hints: true, + show_other_hints: true, + edit_debounce_ms: 0, + scroll_debounce_ms: 0, + show_background: false, + toggle_on_modifiers_press: None, + }); + }); + + let mut cx = EditorLspTestContext::new_rust( + lsp::ServerCapabilities { + hover_provider: Some(lsp::HoverProviderCapability::Simple(true)), + inlay_hint_provider: Some(lsp::OneOf::Right( + lsp::InlayHintServerCapabilities::Options(lsp::InlayHintOptions { + ..Default::default() + }), + )), + ..Default::default() + }, + cx, + ) + .await; + + cx.set_state(indoc! {r#" + fn main() { + let foo = "foo".to_string(); + let bar: String = "bar".to_string();ˇ + } + "#}); + + // Set up inlay hint handler + let buffer_text = cx.buffer_text(); + let hint_position = cx.to_lsp(buffer_text.find("foo =").unwrap() + 3); + cx.set_request_handler::( + move |_, _params, _| async move { + Ok(Some(vec![lsp::InlayHint { + position: hint_position, + label: lsp::InlayHintLabel::String(": String".to_string()), + kind: Some(lsp::InlayHintKind::TYPE), + text_edits: None, + tooltip: None, + padding_left: Some(false), + padding_right: Some(false), + data: None, + }])) + }, + ) + .next() + .await; + + cx.background_executor.run_until_parked(); + + // Verify inlay hint is displayed + cx.update_editor(|editor, _, cx| { + let expected_layers = vec![": String".to_string()]; + assert_eq!(expected_layers, visible_hint_labels(editor, cx)); + }); + + // Set up hover handler that should be called for both inlay hint and explicit type + let hover_count = Arc::new(AtomicUsize::new(0)); + let hover_count_clone = hover_count.clone(); + let _hover_requests = + cx.set_request_handler::(move |_, params, _| { + let count = hover_count_clone.clone(); + async move { + let current = count.fetch_add(1, atomic::Ordering::SeqCst); + println!( + "Hover request {} at position: {:?}", + current + 1, + params.text_document_position_params.position + ); + Ok(Some(lsp::Hover { + contents: lsp::HoverContents::Markup(lsp::MarkupContent { + kind: lsp::MarkupKind::Markdown, + value: + "```rust\nstruct String\n```\n\nA UTF-8 encoded, growable string." + .to_string(), + }), + range: None, + })) + } + }); + + // Test hovering over the inlay hint type + // Get the position where the inlay hint is displayed + let inlay_range = cx + .ranges(indoc! {r#" + fn main() { + let foo« »= "foo".to_string(); + let bar: String = "bar".to_string(); + } + "#}) + .first() + .cloned() + .unwrap(); + + // Create a PointForPosition that simulates hovering over the inlay hint + let point_for_position = cx.update_editor(|editor, window, cx| { + let snapshot = editor.snapshot(window, cx); + let previous_valid = inlay_range.start.to_display_point(&snapshot); + let next_valid = inlay_range.end.to_display_point(&snapshot); + // Position over the "S" in "String" of the inlay hint ": String" + let exact_unclipped = DisplayPoint::new( + previous_valid.row(), + previous_valid.column() + 2, // Skip past ": " to hover over "String" + ); + PointForPosition { + previous_valid, + next_valid, + exact_unclipped, + column_overshoot_after_line_end: 0, + } + }); + + // Update hovered link to trigger hover logic for inlay hints + cx.update_editor(|editor, window, cx| { + let snapshot = editor.snapshot(window, cx); + update_inlay_link_and_hover_points( + &snapshot, + point_for_position, + editor, + false, // secondary_held + false, // shift_held + window, + cx, + ); + }); + + // Wait for potential hover popover + cx.background_executor + .advance_clock(Duration::from_millis(get_hover_popover_delay(&cx) + 100)); + cx.background_executor.run_until_parked(); + + // Check if hover request was made for inlay hint + let inlay_hover_request_count = hover_count.load(atomic::Ordering::SeqCst); + println!( + "Hover requests after inlay hint hover: {}", + inlay_hover_request_count + ); + + // Check if hover popover is shown for inlay hint + let has_inlay_hover = cx.editor(|editor, _, _| { + println!( + "Inlay hint hover - info_popovers: {}", + editor.hover_state.info_popovers.len() + ); + println!( + "Inlay hint hover - info_task: {:?}", + editor.hover_state.info_task.is_some() + ); + editor.hover_state.info_popovers.len() > 0 + }); + + // Clear hover state + cx.update_editor(|editor, _, cx| { + hide_hover(editor, cx); + }); + + // Test hovering over the explicit type + // Find the position of "String" in the explicit type annotation + let explicit_string_point = cx.display_point(indoc! {r#" + fn main() { + let foo = "foo".to_string(); + let bar: Sˇtring = "bar".to_string(); + } + "#}); + + // Use hover_at to trigger hover on explicit type + cx.update_editor(|editor, window, cx| { + let snapshot = editor.snapshot(window, cx); + let anchor = snapshot + .buffer_snapshot + .anchor_before(explicit_string_point.to_offset(&snapshot, Bias::Left)); + hover_at(editor, Some(anchor), window, cx); + }); + + // Wait for hover request + cx.background_executor + .advance_clock(Duration::from_millis(get_hover_popover_delay(&cx) + 100)); + cx.background_executor.run_until_parked(); + + // Check if hover popover is shown for explicit type + let has_explicit_hover = cx.editor(|editor, _, _| { + println!( + "Explicit type hover - info_popovers: {}", + editor.hover_state.info_popovers.len() + ); + println!( + "Explicit type hover - info_task: {:?}", + editor.hover_state.info_task.is_some() + ); + editor.hover_state.info_popovers.len() > 0 + }); + + // Check total hover requests + let total_requests = hover_count.load(atomic::Ordering::SeqCst); + println!("Total hover requests: {}", total_requests); + + // The test should fail here - inlay hints don't show hover popovers but explicit types do + println!("Has inlay hover: {}", has_inlay_hover); + println!("Has explicit hover: {}", has_explicit_hover); + + // This test demonstrates issue #33715: hovering over type information in inlay hints + // does not show hover popovers, while hovering over explicit types does. + + // Expected behavior: Both should show hover popovers + // Actual behavior: Only explicit types show hover popovers + + assert!( + has_explicit_hover, + "Hover popover should be shown when hovering over explicit type" + ); + + // This assertion should fail, demonstrating the bug + assert!( + has_inlay_hover, + "Hover popover should be shown when hovering over inlay hint type (Issue #33715). \ + Inlay hint hover requests: {}, Explicit type hover requests: {}", + inlay_hover_request_count, + total_requests - inlay_hover_request_count + ); + } }