ignore Enter keypress when menu has no selection (#1704)

* ignore Enter keypress when menu has no selection

supersedes #1622

Builds on the work in #1285. I want to allow Enter to create a newline
when there is no selection in the autocomplete menu.

This occurs somewhat often when using LSP autocomplete in Elixir which
uses `do/end` blocks (and I set the autocomplete menu delay to 0 which
exacerbates the problem):

```elixir
defmodule MyModule do
  def do_foo(x) do
    x
  end
  def other_function(y) do|
end
```

Here the cursor is `|` in insert mode. The LSP suggests `do_foo` but I
want to create a newline. Hitting Enter currently closes the menu,
so I end up having to hit Enter twice when the module contains any
local with a `do` prefix, which can be inconsistent. With this change,
we ignore the Enter keypress to end up creating the newline in this case.

* pop compositor layer when ignoring Enter keypress

* move closing function out of consumed event result closure

* explicitly label close_fn as an 'Option<Callback>'
This commit is contained in:
Michael Davis 2022-02-27 01:20:21 -06:00 committed by GitHub
parent c1251aecc7
commit 39f7ba36e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
compositor::{Component, Compositor, Context, EventResult}, compositor::{Callback, Component, Compositor, Context, EventResult},
ctrl, key, shift, ctrl, key, shift,
}; };
use crossterm::event::Event; use crossterm::event::Event;
@ -205,16 +205,16 @@ impl<T: Item + 'static> Component for Menu<T> {
_ => return EventResult::Ignored(None), _ => return EventResult::Ignored(None),
}; };
let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _| { let close_fn: Option<Callback> = Some(Box::new(|compositor: &mut Compositor, _| {
// remove the layer // remove the layer
compositor.pop(); compositor.pop();
}))); }));
match event.into() { match event.into() {
// esc or ctrl-c aborts the completion and closes the menu // esc or ctrl-c aborts the completion and closes the menu
key!(Esc) | ctrl!('c') => { key!(Esc) | ctrl!('c') => {
(self.callback_fn)(cx.editor, self.selection(), MenuEvent::Abort); (self.callback_fn)(cx.editor, self.selection(), MenuEvent::Abort);
return close_fn; return EventResult::Consumed(close_fn);
} }
// arrow up/ctrl-p/shift-tab prev completion choice (including updating the doc) // arrow up/ctrl-p/shift-tab prev completion choice (including updating the doc)
shift!(Tab) | key!(Up) | ctrl!('p') | ctrl!('k') => { shift!(Tab) | key!(Up) | ctrl!('p') | ctrl!('k') => {
@ -231,8 +231,10 @@ impl<T: Item + 'static> Component for Menu<T> {
key!(Enter) => { key!(Enter) => {
if let Some(selection) = self.selection() { if let Some(selection) = self.selection() {
(self.callback_fn)(cx.editor, Some(selection), MenuEvent::Validate); (self.callback_fn)(cx.editor, Some(selection), MenuEvent::Validate);
return EventResult::Consumed(close_fn);
} else {
return EventResult::Ignored(close_fn);
} }
return close_fn;
} }
// KeyEvent { // KeyEvent {
// code: KeyCode::Char(c), // code: KeyCode::Char(c),