Allow information popovers while showing autocomplete suggestions (#12157)
Allows information popovers to display while autocomplete suggestions are also being displayed. Before: https://github.com/zed-industries/zed/assets/50590465/a61f0b4e-1521-42de-84fd-c5a3586b74ab After: https://github.com/zed-industries/zed/assets/50590465/ad2daae7-aebc-4c71-ad4c-f9d2deb6d0d0 Release Notes: - Allow hover and completion popovers to appear at the same time ([12152](https://github.com/zed-industries/zed/issues/12152))
This commit is contained in:
@@ -4614,11 +4614,11 @@ impl Element for EditorElement {
|
||||
|
||||
let gutter_settings = EditorSettings::get_global(cx).gutter;
|
||||
|
||||
let mut context_menu_visible = false;
|
||||
let mut _context_menu_visible = false;
|
||||
let mut code_actions_indicator = None;
|
||||
if let Some(newest_selection_head) = newest_selection_head {
|
||||
if (start_row..end_row).contains(&newest_selection_head.row()) {
|
||||
context_menu_visible = self.layout_context_menu(
|
||||
_context_menu_visible = self.layout_context_menu(
|
||||
line_height,
|
||||
&hitbox,
|
||||
&text_hitbox,
|
||||
@@ -4671,7 +4671,7 @@ impl Element for EditorElement {
|
||||
cx,
|
||||
);
|
||||
|
||||
if !context_menu_visible && !cx.has_active_drag() {
|
||||
if !cx.has_active_drag() {
|
||||
self.layout_hover_popovers(
|
||||
&snapshot,
|
||||
&hitbox,
|
||||
|
||||
@@ -612,7 +612,8 @@ impl DiagnosticPopover {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
editor_tests::init_test,
|
||||
actions::ConfirmCompletion,
|
||||
editor_tests::{handle_completion_request, init_test},
|
||||
hover_links::update_inlay_link_and_hover_points,
|
||||
inlay_hint_cache::tests::{cached_hint_labels, visible_hint_labels},
|
||||
test::editor_lsp_test_context::EditorLspTestContext,
|
||||
@@ -625,10 +626,180 @@ mod tests {
|
||||
use lsp::LanguageServerId;
|
||||
use project::{HoverBlock, HoverBlockKind};
|
||||
use smol::stream::StreamExt;
|
||||
use std::sync::atomic;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use text::Bias;
|
||||
use unindent::Unindent;
|
||||
use util::test::marked_text_ranges;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_mouse_hover_info_popover_with_autocomplete_popover(
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) {
|
||||
init_test(cx, |_| {});
|
||||
const HOVER_DELAY_MILLIS: u64 = 350;
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
|
||||
resolve_provider: Some(true),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
let counter = Arc::new(AtomicUsize::new(0));
|
||||
// Basic hover delays and then pops without moving the mouse
|
||||
cx.set_state(indoc! {"
|
||||
oneˇ
|
||||
two
|
||||
three
|
||||
fn test() { println!(); }
|
||||
"});
|
||||
|
||||
//prompt autocompletion menu
|
||||
cx.simulate_keystroke(".");
|
||||
handle_completion_request(
|
||||
&mut cx,
|
||||
indoc! {"
|
||||
one.|<>
|
||||
two
|
||||
three
|
||||
"},
|
||||
vec!["first_completion", "second_completion"],
|
||||
counter.clone(),
|
||||
)
|
||||
.await;
|
||||
cx.condition(|editor, _| editor.context_menu_visible()) // wait until completion menu is visible
|
||||
.await;
|
||||
assert_eq!(counter.load(atomic::Ordering::Acquire), 1); // 1 completion request
|
||||
|
||||
let hover_point = cx.display_point(indoc! {"
|
||||
one.
|
||||
two
|
||||
three
|
||||
fn test() { printˇln!(); }
|
||||
"});
|
||||
cx.update_editor(|editor, cx| {
|
||||
let snapshot = editor.snapshot(cx);
|
||||
let anchor = snapshot
|
||||
.buffer_snapshot
|
||||
.anchor_before(hover_point.to_offset(&snapshot, Bias::Left));
|
||||
hover_at(editor, Some(anchor), cx)
|
||||
});
|
||||
assert!(!cx.editor(|editor, _| editor.hover_state.visible()));
|
||||
|
||||
// After delay, hover should be visible.
|
||||
let symbol_range = cx.lsp_range(indoc! {"
|
||||
one.
|
||||
two
|
||||
three
|
||||
fn test() { «println!»(); }
|
||||
"});
|
||||
let mut requests =
|
||||
cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
|
||||
Ok(Some(lsp::Hover {
|
||||
contents: lsp::HoverContents::Markup(lsp::MarkupContent {
|
||||
kind: lsp::MarkupKind::Markdown,
|
||||
value: "some basic docs".to_string(),
|
||||
}),
|
||||
range: Some(symbol_range),
|
||||
}))
|
||||
});
|
||||
cx.background_executor
|
||||
.advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
|
||||
requests.next().await;
|
||||
|
||||
cx.editor(|editor, _| {
|
||||
assert!(editor.hover_state.visible());
|
||||
assert_eq!(
|
||||
editor.hover_state.info_popovers.len(),
|
||||
1,
|
||||
"Expected exactly one hover but got: {:?}",
|
||||
editor.hover_state.info_popovers
|
||||
);
|
||||
let rendered = editor
|
||||
.hover_state
|
||||
.info_popovers
|
||||
.first()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.parsed_content;
|
||||
assert_eq!(rendered.text, "some basic docs".to_string())
|
||||
});
|
||||
|
||||
// check that the completion menu is still visible and that there still has only been 1 completion request
|
||||
cx.editor(|editor, _| assert!(editor.context_menu_visible()));
|
||||
assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
|
||||
|
||||
//apply a completion and check it was successfully applied
|
||||
let _apply_additional_edits = cx.update_editor(|editor, cx| {
|
||||
editor.context_menu_next(&Default::default(), cx);
|
||||
editor
|
||||
.confirm_completion(&ConfirmCompletion::default(), cx)
|
||||
.unwrap()
|
||||
});
|
||||
cx.assert_editor_state(indoc! {"
|
||||
one.second_completionˇ
|
||||
two
|
||||
three
|
||||
fn test() { println!(); }
|
||||
"});
|
||||
|
||||
// check that the completion menu is no longer visible and that there still has only been 1 completion request
|
||||
cx.editor(|editor, _| assert!(!editor.context_menu_visible()));
|
||||
assert_eq!(counter.load(atomic::Ordering::Acquire), 1);
|
||||
|
||||
//verify the information popover is still visible and unchanged
|
||||
cx.editor(|editor, _| {
|
||||
assert!(editor.hover_state.visible());
|
||||
assert_eq!(
|
||||
editor.hover_state.info_popovers.len(),
|
||||
1,
|
||||
"Expected exactly one hover but got: {:?}",
|
||||
editor.hover_state.info_popovers
|
||||
);
|
||||
let rendered = editor
|
||||
.hover_state
|
||||
.info_popovers
|
||||
.first()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.parsed_content;
|
||||
assert_eq!(rendered.text, "some basic docs".to_string())
|
||||
});
|
||||
|
||||
// Mouse moved with no hover response dismisses
|
||||
let hover_point = cx.display_point(indoc! {"
|
||||
one.second_completionˇ
|
||||
two
|
||||
three
|
||||
fn teˇst() { println!(); }
|
||||
"});
|
||||
let mut request = cx
|
||||
.lsp
|
||||
.handle_request::<lsp::request::HoverRequest, _, _>(|_, _| async move { Ok(None) });
|
||||
cx.update_editor(|editor, cx| {
|
||||
let snapshot = editor.snapshot(cx);
|
||||
let anchor = snapshot
|
||||
.buffer_snapshot
|
||||
.anchor_before(hover_point.to_offset(&snapshot, Bias::Left));
|
||||
hover_at(editor, Some(anchor), cx)
|
||||
});
|
||||
cx.background_executor
|
||||
.advance_clock(Duration::from_millis(HOVER_DELAY_MILLIS + 100));
|
||||
request.next().await;
|
||||
|
||||
// verify that the information popover is no longer visible
|
||||
cx.editor(|editor, _| {
|
||||
assert!(!editor.hover_state.visible());
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_mouse_hover_info_popover(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
Reference in New Issue
Block a user