editor: Hide hover popover when code actions context menu is triggered (#31042)

This PR hides hover info/diagnostic popovers when code action menu is
shown. We already hide hover info/diagnostic popover on code completion
menu trigger (handled on input).

Note: It is still possible to see hover popover if code completion or
code action menu is already open. This is intended behavior.

- [x] Test hover popover hides when code action is triggered

Release Notes:

- Fixed issue where info and diagnostic hover popovers were still
visible when code action menu is triggered.
This commit is contained in:
smit 2025-05-21 03:31:35 +05:30 committed by GitHub
parent 4bb04cef9d
commit d547a86e31
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 144 additions and 1 deletions

View file

@ -5081,7 +5081,7 @@ impl Editor {
if editor.focus_handle.is_focused(window) && menu.is_some() {
let mut menu = menu.unwrap();
menu.resolve_visible_completions(editor.completion_provider.as_deref(), cx);
crate::hover_popover::hide_hover(editor, cx);
*editor.context_menu.borrow_mut() =
Some(CodeContextMenu::Completions(menu));
@ -5512,6 +5512,7 @@ impl Editor {
.map_or(true, |actions| actions.is_empty())
&& debug_scenarios.is_empty();
if let Ok(task) = editor.update_in(cx, |editor, window, cx| {
crate::hover_popover::hide_hover(editor, cx);
*editor.context_menu.borrow_mut() =
Some(CodeContextMenu::CodeActions(CodeActionsMenu {
buffer,

View file

@ -13980,6 +13980,148 @@ async fn test_completions_resolve_updates_labels_if_filter_text_matches(cx: &mut
});
}
#[gpui::test]
async fn test_context_menus_hide_hover_popover(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorLspTestContext::new_rust(
lsp::ServerCapabilities {
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
code_action_provider: Some(lsp::CodeActionProviderCapability::Simple(true)),
completion_provider: Some(lsp::CompletionOptions {
resolve_provider: Some(true),
..Default::default()
}),
..Default::default()
},
cx,
)
.await;
cx.set_state(indoc! {"
struct TestStruct {
field: i32
}
fn mainˇ() {
let unused_var = 42;
let test_struct = TestStruct { field: 42 };
}
"});
let symbol_range = cx.lsp_range(indoc! {"
struct TestStruct {
field: i32
}
«fn main»() {
let unused_var = 42;
let test_struct = TestStruct { field: 42 };
}
"});
let mut hover_requests =
cx.set_request_handler::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
Ok(Some(lsp::Hover {
contents: lsp::HoverContents::Markup(lsp::MarkupContent {
kind: lsp::MarkupKind::Markdown,
value: "Function documentation".to_string(),
}),
range: Some(symbol_range),
}))
});
// Case 1: Test that code action menu hide hover popover
cx.dispatch_action(Hover);
hover_requests.next().await;
cx.condition(|editor, _| editor.hover_state.visible()).await;
let mut code_action_requests = cx.set_request_handler::<lsp::request::CodeActionRequest, _, _>(
move |_, _, _| async move {
Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
lsp::CodeAction {
title: "Remove unused variable".to_string(),
kind: Some(CodeActionKind::QUICKFIX),
edit: Some(lsp::WorkspaceEdit {
changes: Some(
[(
lsp::Url::from_file_path(path!("/file.rs")).unwrap(),
vec![lsp::TextEdit {
range: lsp::Range::new(
lsp::Position::new(5, 4),
lsp::Position::new(5, 27),
),
new_text: "".to_string(),
}],
)]
.into_iter()
.collect(),
),
..Default::default()
}),
..Default::default()
},
)]))
},
);
cx.update_editor(|editor, window, cx| {
editor.toggle_code_actions(
&ToggleCodeActions {
deployed_from_indicator: None,
quick_launch: false,
},
window,
cx,
);
});
code_action_requests.next().await;
cx.run_until_parked();
cx.condition(|editor, _| editor.context_menu_visible())
.await;
cx.update_editor(|editor, _, _| {
assert!(
!editor.hover_state.visible(),
"Hover popover should be hidden when code action menu is shown"
);
// Hide code actions
editor.context_menu.take();
});
// Case 2: Test that code completions hide hover popover
cx.dispatch_action(Hover);
hover_requests.next().await;
cx.condition(|editor, _| editor.hover_state.visible()).await;
let counter = Arc::new(AtomicUsize::new(0));
let mut completion_requests =
cx.set_request_handler::<lsp::request::Completion, _, _>(move |_, _, _| {
let counter = counter.clone();
async move {
counter.fetch_add(1, atomic::Ordering::Release);
Ok(Some(lsp::CompletionResponse::Array(vec![
lsp::CompletionItem {
label: "main".into(),
kind: Some(lsp::CompletionItemKind::FUNCTION),
detail: Some("() -> ()".to_string()),
..Default::default()
},
lsp::CompletionItem {
label: "TestStruct".into(),
kind: Some(lsp::CompletionItemKind::STRUCT),
detail: Some("struct TestStruct".to_string()),
..Default::default()
},
])))
}
});
cx.update_editor(|editor, window, cx| {
editor.show_completions(&ShowCompletions { trigger: None }, window, cx);
});
completion_requests.next().await;
cx.condition(|editor, _| editor.context_menu_visible())
.await;
cx.update_editor(|editor, _, _| {
assert!(
!editor.hover_state.visible(),
"Hover popover should be hidden when completion menu is shown"
);
});
}
#[gpui::test]
async fn test_completions_resolve_happens_once(cx: &mut TestAppContext) {
init_test(cx, |_| {});