Hide signature help if it overlays completion menu (#5523)

pull/6476/head^2
Pascal Kuthe 1 year ago committed by Blaž Hrastnik
parent ab819d80f1
commit 7a69c40524
No known key found for this signature in database
GPG Key ID: 1238B9C4AD889640

@ -54,8 +54,8 @@ use crate::{
job::Callback, job::Callback,
keymap::ReverseKeymap, keymap::ReverseKeymap,
ui::{ ui::{
self, editor::InsertEvent, overlay::overlayed, FilePicker, Picker, Popup, Prompt, self, editor::InsertEvent, lsp::SignatureHelp, overlay::overlayed, FilePicker, Picker,
PromptEvent, Popup, Prompt, PromptEvent,
}, },
}; };
@ -4261,7 +4261,7 @@ pub fn completion(cx: &mut Context) {
} }
let size = compositor.size(); let size = compositor.size();
let ui = compositor.find::<ui::EditorView>().unwrap(); let ui = compositor.find::<ui::EditorView>().unwrap();
ui.set_completion( let completion_area = ui.set_completion(
editor, editor,
savepoint, savepoint,
items, items,
@ -4270,6 +4270,15 @@ pub fn completion(cx: &mut Context) {
trigger_offset, trigger_offset,
size, size,
); );
let size = compositor.size();
let signature_help_area = compositor
.find_id::<Popup<SignatureHelp>>(SignatureHelp::ID)
.map(|signature_help| signature_help.area(size, editor));
// Delete the signature help popup if they intersect.
if matches!((completion_area, signature_help_area),(Some(a), Some(b)) if a.intersects(b))
{
compositor.remove(SignatureHelp::ID);
}
}, },
); );
} }

@ -1221,10 +1221,25 @@ pub fn signature_help_impl(cx: &mut Context, invoked: SignatureHelpInvoked) {
contents.set_active_param_range(active_param_range()); contents.set_active_param_range(active_param_range());
let old_popup = compositor.find_id::<Popup<SignatureHelp>>(SignatureHelp::ID); let old_popup = compositor.find_id::<Popup<SignatureHelp>>(SignatureHelp::ID);
let popup = Popup::new(SignatureHelp::ID, contents) let mut popup = Popup::new(SignatureHelp::ID, contents)
.position(old_popup.and_then(|p| p.get_position())) .position(old_popup.and_then(|p| p.get_position()))
.position_bias(Open::Above) .position_bias(Open::Above)
.ignore_escape_key(true); .ignore_escape_key(true);
// Don't create a popup if it intersects the auto-complete menu.
let size = compositor.size();
if compositor
.find::<ui::EditorView>()
.unwrap()
.completion
.as_mut()
.map(|completion| completion.area(size, editor))
.filter(|area| area.intersects(popup.area(size, editor)))
.is_some()
{
return;
}
compositor.replace_or_push(SignatureHelp::ID, popup); compositor.replace_or_push(SignatureHelp::ID, popup);
}, },
); );

@ -414,6 +414,10 @@ impl Completion {
true true
} }
pub fn area(&mut self, viewport: Rect, editor: &Editor) -> Rect {
self.popup.area(viewport, editor)
}
} }
impl Component for Completion { impl Component for Completion {
@ -481,7 +485,7 @@ impl Component for Completion {
}; };
let popup_area = { let popup_area = {
let (popup_x, popup_y) = self.popup.get_rel_position(area, cx); let (popup_x, popup_y) = self.popup.get_rel_position(area, cx.editor);
let (popup_width, popup_height) = self.popup.get_size(); let (popup_width, popup_height) = self.popup.get_size();
Rect::new(popup_x, popup_y, popup_width, popup_height) Rect::new(popup_x, popup_y, popup_width, popup_height)
}; };

@ -952,7 +952,7 @@ impl EditorView {
start_offset: usize, start_offset: usize,
trigger_offset: usize, trigger_offset: usize,
size: Rect, size: Rect,
) { ) -> Option<Rect> {
let mut completion = Completion::new( let mut completion = Completion::new(
editor, editor,
savepoint, savepoint,
@ -964,15 +964,17 @@ impl EditorView {
if completion.is_empty() { if completion.is_empty() {
// skip if we got no completion results // skip if we got no completion results
return; return None;
} }
let area = completion.area(size, editor);
editor.last_completion = None; editor.last_completion = None;
self.last_insert.1.push(InsertEvent::TriggerCompletion); self.last_insert.1.push(InsertEvent::TriggerCompletion);
// TODO : propagate required size on resize to completion too // TODO : propagate required size on resize to completion too
completion.required_size((size.width, size.height)); completion.required_size((size.width, size.height));
self.completion = Some(completion); self.completion = Some(completion);
Some(area)
} }
pub fn clear_completion(&mut self, editor: &mut Editor) { pub fn clear_completion(&mut self, editor: &mut Editor) {
@ -1256,13 +1258,15 @@ impl Component for EditorView {
// let completion swallow the event if necessary // let completion swallow the event if necessary
let mut consumed = false; let mut consumed = false;
if let Some(completion) = &mut self.completion { if let Some(completion) = &mut self.completion {
// use a fake context here let res = {
let mut cx = Context { // use a fake context here
editor: cx.editor, let mut cx = Context {
jobs: cx.jobs, editor: cx.editor,
scroll: None, jobs: cx.jobs,
scroll: None,
};
completion.handle_event(event, &mut cx)
}; };
let res = completion.handle_event(event, &mut cx);
if let EventResult::Consumed(callback) = res { if let EventResult::Consumed(callback) = res {
consumed = true; consumed = true;
@ -1270,6 +1274,12 @@ impl Component for EditorView {
if callback.is_some() { if callback.is_some() {
// assume close_fn // assume close_fn
self.clear_completion(cx.editor); self.clear_completion(cx.editor);
// In case the popup was deleted because of an intersection w/ the auto-complete menu.
commands::signature_help_impl(
&mut cx,
commands::SignatureHelpInvoked::Automatic,
);
} }
} }
} }

@ -6,7 +6,10 @@ use crate::{
use tui::buffer::Buffer as Surface; use tui::buffer::Buffer as Surface;
use helix_core::Position; use helix_core::Position;
use helix_view::graphics::{Margin, Rect}; use helix_view::{
graphics::{Margin, Rect},
Editor,
};
// TODO: share logic with Menu, it's essentially Popup(render_fn), but render fn needs to return // TODO: share logic with Menu, it's essentially Popup(render_fn), but render fn needs to return
// a width/height hint. maybe Popup(Box<Component>) // a width/height hint. maybe Popup(Box<Component>)
@ -88,10 +91,10 @@ impl<T: Component> Popup<T> {
/// Calculate the position where the popup should be rendered and return the coordinates of the /// Calculate the position where the popup should be rendered and return the coordinates of the
/// top left corner. /// top left corner.
pub fn get_rel_position(&mut self, viewport: Rect, cx: &Context) -> (u16, u16) { pub fn get_rel_position(&mut self, viewport: Rect, editor: &Editor) -> (u16, u16) {
let position = self let position = self
.position .position
.get_or_insert_with(|| cx.editor.cursor().0.unwrap_or_default()); .get_or_insert_with(|| editor.cursor().0.unwrap_or_default());
let (width, height) = self.size; let (width, height) = self.size;
@ -155,6 +158,16 @@ impl<T: Component> Popup<T> {
pub fn contents_mut(&mut self) -> &mut T { pub fn contents_mut(&mut self) -> &mut T {
&mut self.contents &mut self.contents
} }
pub fn area(&mut self, viewport: Rect, editor: &Editor) -> Rect {
// trigger required_size so we recalculate if the child changed
self.required_size((viewport.width, viewport.height));
let (rel_x, rel_y) = self.get_rel_position(viewport, editor);
// clip to viewport
viewport.intersection(Rect::new(rel_x, rel_y, self.size.0, self.size.1))
}
} }
impl<T: Component> Component for Popup<T> { impl<T: Component> Component for Popup<T> {
@ -232,16 +245,9 @@ impl<T: Component> Component for Popup<T> {
} }
fn render(&mut self, viewport: Rect, surface: &mut Surface, cx: &mut Context) { fn render(&mut self, viewport: Rect, surface: &mut Surface, cx: &mut Context) {
// trigger required_size so we recalculate if the child changed let area = self.area(viewport, cx.editor);
self.required_size((viewport.width, viewport.height));
cx.scroll = Some(self.scroll); cx.scroll = Some(self.scroll);
let (rel_x, rel_y) = self.get_rel_position(viewport, cx);
// clip to viewport
let area = viewport.intersection(Rect::new(rel_x, rel_y, self.size.0, self.size.1));
// clear area // clear area
let background = cx.editor.theme.get("ui.popup"); let background = cx.editor.theme.get("ui.popup");
surface.clear_with(area, background); surface.clear_with(area, background);

Loading…
Cancel
Save