on master help sign in
Hide signature help if it overlays completion menu (#5523)
7a69c40Pascal Kuthe
helix-term/src/commands.rs+12−3
@@ -54,8 +54,8 @@ use crate::{
54 job::Callback,
54 job::Callback,
55 keymap::ReverseKeymap,
55 keymap::ReverseKeymap,
56 ui::{
56 ui::{
57− self, editor::InsertEvent, overlay::overlayed, FilePicker, Picker, Popup, Prompt,
57+ self, editor::InsertEvent, lsp::SignatureHelp, overlay::overlayed, FilePicker, Picker,
58− PromptEvent,
58+ Popup, Prompt, PromptEvent,
59 },
59 },
60};
60};
61
61
@@ -4261,7 +4261,7 @@ pub fn completion(cx: &mut Context) {
4261 }
4261 }
4262 let size = compositor.size();
4262 let size = compositor.size();
4263 let ui = compositor.find::<ui::EditorView>().unwrap();
4263 let ui = compositor.find::<ui::EditorView>().unwrap();
4264− ui.set_completion(
4264+ let completion_area = ui.set_completion(
4265 editor,
4265 editor,
4266 savepoint,
4266 savepoint,
4267 items,
4267 items,
@@ -4270,6 +4270,15 @@ pub fn completion(cx: &mut Context) {
4270 trigger_offset,
4270 trigger_offset,
4271 size,
4271 size,
4272 );
4272 );
4273+ let size = compositor.size();
4274+ let signature_help_area = compositor
4275+ .find_id::<Popup<SignatureHelp>>(SignatureHelp::ID)
4276+ .map(|signature_help| signature_help.area(size, editor));
4277+ // Delete the signature help popup if they intersect.
4278+ if matches!((completion_area, signature_help_area),(Some(a), Some(b)) if a.intersects(b))
4279+ {
4280+ compositor.remove(SignatureHelp::ID);
4281+ }
4273 },
4282 },
4274 );
4283 );
4275}
4284}
helix-term/src/commands/lsp.rs+16−1
@@ -1221,10 +1221,25 @@ pub fn signature_help_impl(cx: &mut Context, invoked: SignatureHelpInvoked) {
1221 contents.set_active_param_range(active_param_range());
1221 contents.set_active_param_range(active_param_range());
1222
1222
1223 let old_popup = compositor.find_id::<Popup<SignatureHelp>>(SignatureHelp::ID);
1223 let old_popup = compositor.find_id::<Popup<SignatureHelp>>(SignatureHelp::ID);
1224− let popup = Popup::new(SignatureHelp::ID, contents)
1224+ let mut popup = Popup::new(SignatureHelp::ID, contents)
1225 .position(old_popup.and_then(|p| p.get_position()))
1225 .position(old_popup.and_then(|p| p.get_position()))
1226 .position_bias(Open::Above)
1226 .position_bias(Open::Above)
1227 .ignore_escape_key(true);
1227 .ignore_escape_key(true);
1228+
1229+ // Don't create a popup if it intersects the auto-complete menu.
1230+ let size = compositor.size();
1231+ if compositor
1232+ .find::<ui::EditorView>()
1233+ .unwrap()
1234+ .completion
1235+ .as_mut()
1236+ .map(|completion| completion.area(size, editor))
1237+ .filter(|area| area.intersects(popup.area(size, editor)))
1238+ .is_some()
1239+ {
1240+ return;
1241+ }
1242+
1228 compositor.replace_or_push(SignatureHelp::ID, popup);
1243 compositor.replace_or_push(SignatureHelp::ID, popup);
1229 },
1244 },
1230 );
1245 );
helix-term/src/ui/completion.rs+5−1
@@ -414,6 +414,10 @@ impl Completion {
414
414
415 true
415 true
416 }
416 }
417+
418+ pub fn area(&mut self, viewport: Rect, editor: &Editor) -> Rect {
419+ self.popup.area(viewport, editor)
420+ }
417}
421}
418
422
419impl Component for Completion {
423impl Component for Completion {
@@ -481,7 +485,7 @@ impl Component for Completion {
481 };
485 };
482
486
483 let popup_area = {
487 let popup_area = {
484− let (popup_x, popup_y) = self.popup.get_rel_position(area, cx);
488+ let (popup_x, popup_y) = self.popup.get_rel_position(area, cx.editor);
485 let (popup_width, popup_height) = self.popup.get_size();
489 let (popup_width, popup_height) = self.popup.get_size();
486 Rect::new(popup_x, popup_y, popup_width, popup_height)
490 Rect::new(popup_x, popup_y, popup_width, popup_height)
487 };
491 };
helix-term/src/ui/editor.rs+18−8
@@ -952,7 +952,7 @@ impl EditorView {
952 start_offset: usize,
952 start_offset: usize,
953 trigger_offset: usize,
953 trigger_offset: usize,
954 size: Rect,
954 size: Rect,
955− ) {
955+ ) -> Option<Rect> {
956 let mut completion = Completion::new(
956 let mut completion = Completion::new(
957 editor,
957 editor,
958 savepoint,
958 savepoint,
@@ -964,15 +964,17 @@ impl EditorView {
964
964
965 if completion.is_empty() {
965 if completion.is_empty() {
966 // skip if we got no completion results
966 // skip if we got no completion results
967− return;
967+ return None;
968 }
968 }
969
969
970+ let area = completion.area(size, editor);
970 editor.last_completion = None;
971 editor.last_completion = None;
971 self.last_insert.1.push(InsertEvent::TriggerCompletion);
972 self.last_insert.1.push(InsertEvent::TriggerCompletion);
972
973
973 // TODO : propagate required size on resize to completion too
974 // TODO : propagate required size on resize to completion too
974 completion.required_size((size.width, size.height));
975 completion.required_size((size.width, size.height));
975 self.completion = Some(completion);
976 self.completion = Some(completion);
977+ Some(area)
976 }
978 }
977
979
978 pub fn clear_completion(&mut self, editor: &mut Editor) {
980 pub fn clear_completion(&mut self, editor: &mut Editor) {
@@ -1256,13 +1258,15 @@ impl Component for EditorView {
1256 // let completion swallow the event if necessary
1258 // let completion swallow the event if necessary
1257 let mut consumed = false;
1259 let mut consumed = false;
1258 if let Some(completion) = &mut self.completion {
1260 if let Some(completion) = &mut self.completion {
1261+ let res = {
1259− // use a fake context here
1262+ // use a fake context here
1260− let mut cx = Context {
1263+ let mut cx = Context {
1261− editor: cx.editor,
1264+ editor: cx.editor,
1262− jobs: cx.jobs,
1265+ jobs: cx.jobs,
1263− scroll: None,
1266+ scroll: None,
1267+ };
1268+ completion.handle_event(event, &mut cx)
1264 };
1269 };
1265− let res = completion.handle_event(event, &mut cx);
1266
1270
1267 if let EventResult::Consumed(callback) = res {
1271 if let EventResult::Consumed(callback) = res {
1268 consumed = true;
1272 consumed = true;
@@ -1270,6 +1274,12 @@ impl Component for EditorView {
1270 if callback.is_some() {
1274 if callback.is_some() {
1271 // assume close_fn
1275 // assume close_fn
1272 self.clear_completion(cx.editor);
1276 self.clear_completion(cx.editor);
1277+
1278+ // In case the popup was deleted because of an intersection w/ the auto-complete menu.
1279+ commands::signature_help_impl(
1280+ &mut cx,
1281+ commands::SignatureHelpInvoked::Automatic,
1282+ );
1273 }
1283 }
1274 }
1284 }
1275 }
1285 }
helix-term/src/ui/popup.rs+17−11
@@ -6,7 +6,10 @@ use crate::{
6use tui::buffer::Buffer as Surface;
6use tui::buffer::Buffer as Surface;
7
7
8use helix_core::Position;
8use helix_core::Position;
9+use helix_view::{
9−use helix_view::graphics::{Margin, Rect};
10+ graphics::{Margin, Rect},
11+ Editor,
12+};
10
13
11// TODO: share logic with Menu, it's essentially Popup(render_fn), but render fn needs to return
14// TODO: share logic with Menu, it's essentially Popup(render_fn), but render fn needs to return
12// a width/height hint. maybe Popup(Box<Component>)
15// a width/height hint. maybe Popup(Box<Component>)
@@ -88,10 +91,10 @@ impl<T: Component> Popup<T> {
88
91
89 /// Calculate the position where the popup should be rendered and return the coordinates of the
92 /// Calculate the position where the popup should be rendered and return the coordinates of the
90 /// top left corner.
93 /// top left corner.
91− pub fn get_rel_position(&mut self, viewport: Rect, cx: &Context) -> (u16, u16) {
94+ pub fn get_rel_position(&mut self, viewport: Rect, editor: &Editor) -> (u16, u16) {
92 let position = self
95 let position = self
93 .position
96 .position
94− .get_or_insert_with(|| cx.editor.cursor().0.unwrap_or_default());
97+ .get_or_insert_with(|| editor.cursor().0.unwrap_or_default());
95
98
96 let (width, height) = self.size;
99 let (width, height) = self.size;
97
100
@@ -155,6 +158,16 @@ impl<T: Component> Popup<T> {
155 pub fn contents_mut(&mut self) -> &mut T {
158 pub fn contents_mut(&mut self) -> &mut T {
156 &mut self.contents
159 &mut self.contents
157 }
160 }
161+
162+ pub fn area(&mut self, viewport: Rect, editor: &Editor) -> Rect {
163+ // trigger required_size so we recalculate if the child changed
164+ self.required_size((viewport.width, viewport.height));
165+
166+ let (rel_x, rel_y) = self.get_rel_position(viewport, editor);
167+
168+ // clip to viewport
169+ viewport.intersection(Rect::new(rel_x, rel_y, self.size.0, self.size.1))
170+ }
158}
171}
159
172
160impl<T: Component> Component for Popup<T> {
173impl<T: Component> Component for Popup<T> {
@@ -232,16 +245,9 @@ impl<T: Component> Component for Popup<T> {
232 }
245 }
233
246
234 fn render(&mut self, viewport: Rect, surface: &mut Surface, cx: &mut Context) {
247 fn render(&mut self, viewport: Rect, surface: &mut Surface, cx: &mut Context) {
235− // trigger required_size so we recalculate if the child changed
248+ let area = self.area(viewport, cx.editor);
236− self.required_size((viewport.width, viewport.height));
237−
238 cx.scroll = Some(self.scroll);
249 cx.scroll = Some(self.scroll);
239
250
240− let (rel_x, rel_y) = self.get_rel_position(viewport, cx);
241−
242− // clip to viewport
243− let area = viewport.intersection(Rect::new(rel_x, rel_y, self.size.0, self.size.1));
244−
245 // clear area
251 // clear area
246 let background = cx.editor.theme.get("ui.popup");
252 let background = cx.editor.theme.get("ui.popup");
247 surface.clear_with(area, background);
253 surface.clear_with(area, background);