From f99bea404f43ea0e373fd9fe54616d3282e8364b Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Sun, 22 Aug 2021 15:28:45 +0900 Subject: idle timer wip --- helix-term/src/ui/editor.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'helix-term/src/ui') diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 0605e2c7..aa2d6636 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -901,6 +901,7 @@ impl Component for EditorView { EventResult::Consumed(None) } Event::Key(key) => { + cxt.editor.reset_idle_timer(); let mut key = KeyEvent::from(key); canonicalize_key(&mut key); // clear status -- cgit v1.2.3-70-g09d2 From 40abec80e1062503d70055ed9e968cd4b31411a7 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Thu, 26 Aug 2021 11:14:46 +0900 Subject: Experiment with autocompletion on idle --- helix-term/src/application.rs | 38 ++++++++++++++++++++++++++++++++++++-- helix-term/src/commands.rs | 2 +- helix-term/src/ui/editor.rs | 2 +- 3 files changed, 38 insertions(+), 4 deletions(-) (limited to 'helix-term/src/ui') diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index dbd8755d..ab268041 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -200,9 +200,9 @@ impl Application { self.render(); } _ = &mut self.editor.idle_timer => { + // idle timeout self.editor.clear_idle_timer(); - println!("idle!") - // idle timeout + self.handle_idle_timeout(); } } } @@ -233,6 +233,40 @@ impl Application { } } + pub fn handle_idle_timeout(&mut self) { + use helix_view::document::Mode; + use crate::commands::{Context, completion}; + + + if doc_mut!(self.editor).mode != Mode::Insert { + return; + } + let editor_view = self + .compositor + .find(std::any::type_name::()) + .expect("expected at least one EditorView"); + let editor_view = editor_view + .as_any_mut() + .downcast_mut::() + .unwrap(); + + if editor_view.completion.is_some() { + return; + } + + let mut cx = Context { + selected_register: helix_view::RegisterSelection::default(), + editor: &mut self.editor, + jobs: &mut self.jobs, + count: None, + callback: None, + on_next_key_callback: None, + }; + completion(&mut cx); + // TODO: scan backwards for trigger and filter the box + self.render(); + } + pub fn handle_terminal_events(&mut self, event: Option>) { let mut cx = crate::compositor::Context { editor: &mut self.editor, diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index ec76c81d..1dbb5616 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4045,7 +4045,7 @@ fn remove_primary_selection(cx: &mut Context) { doc.set_selection(view.id, selection); } -fn completion(cx: &mut Context) { +pub fn completion(cx: &mut Context) { // trigger on trigger char, or if user calls it // (or on word char typing??) // after it's triggered, if response marked is_incomplete, update on every subsequent keypress diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index aa2d6636..ee3b3c65 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -33,7 +33,7 @@ pub struct EditorView { keymaps: Keymaps, on_next_key: Option>, last_insert: (commands::Command, Vec), - completion: Option, + pub(crate) completion: Option, spinners: ProgressSpinners, autoinfo: Option, } -- cgit v1.2.3-70-g09d2 From 66f26e82ceaf4b81dfeb429dcb840a3242e8f254 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Fri, 27 Aug 2021 10:44:12 +0900 Subject: Filter the initial completion --- helix-term/src/application.rs | 11 ++++++----- helix-term/src/commands.rs | 17 ++++++++++++----- helix-term/src/ui/completion.rs | 25 +++++++++++++++++++------ helix-term/src/ui/editor.rs | 5 ++++- helix-view/src/macros.rs | 16 ++++++++++++++++ 5 files changed, 57 insertions(+), 17 deletions(-) (limited to 'helix-term/src/ui') diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index ab268041..79921268 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -234,9 +234,8 @@ impl Application { } pub fn handle_idle_timeout(&mut self) { + use crate::commands::{completion, Context}; use helix_view::document::Mode; - use crate::commands::{Context, completion}; - if doc_mut!(self.editor).mode != Mode::Insert { return; @@ -254,6 +253,8 @@ impl Application { return; } + // TODO: if completion window was closed with enter (and no selection) we shouldn't retrigger + let mut cx = Context { selected_register: helix_view::RegisterSelection::default(), editor: &mut self.editor, @@ -262,9 +263,9 @@ impl Application { callback: None, on_next_key_callback: None, }; - completion(&mut cx); - // TODO: scan backwards for trigger and filter the box - self.render(); + completion(&mut cx); + // TODO: scan backwards for trigger and filter the box + self.render(); } pub fn handle_terminal_events(&mut self, event: Option>) { diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 1dbb5616..3798c99a 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4090,10 +4090,8 @@ pub fn completion(cx: &mut Context) { }; let offset_encoding = language_server.offset_encoding(); - let cursor = doc - .selection(view.id) - .primary() - .cursor(doc.text().slice(..)); + let text = doc.text().slice(..); + let cursor = doc.selection(view.id).primary().cursor(text); let pos = pos_to_lsp_pos(doc.text(), cursor, offset_encoding); @@ -4101,6 +4099,15 @@ pub fn completion(cx: &mut Context) { let trigger_offset = cursor; + // TODO: trigger_offset should be the cursor offset but we also need a starting offset from where we want to apply + // completion filtering. For example logger.te| should filter the initial suggestion list with "te". + + use helix_core::chars; + let mut iter = text.chars_at(cursor); + iter.reverse(); + let offset = iter.take_while(|ch| chars::char_is_word(*ch)).count(); + let start_offset = cursor.saturating_sub(offset); + cx.callback( future, move |editor: &mut Editor, @@ -4131,7 +4138,7 @@ pub fn completion(cx: &mut Context) { .find(std::any::type_name::()) .unwrap(); if let Some(ui) = ui.as_any_mut().downcast_mut::() { - ui.set_completion(items, offset_encoding, trigger_offset, size); + ui.set_completion(editor, items, offset_encoding, start_offset, trigger_offset, size); }; }, ); diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 6c9e3a80..72b03274 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -69,14 +69,17 @@ impl menu::Item for CompletionItem { /// Wraps a Menu. pub struct Completion { popup: Popup>, + start_offset: usize, trigger_offset: usize, // TODO: maintain a completioncontext with trigger kind & trigger char } impl Completion { pub fn new( + editor: &Editor, items: Vec, offset_encoding: helix_lsp::OffsetEncoding, + start_offset: usize, trigger_offset: usize, ) -> Self { // let items: Vec = Vec::new(); @@ -175,16 +178,22 @@ impl Completion { }; }); let popup = Popup::new(menu); - Self { + let mut completion = Self { popup, + start_offset, trigger_offset, - } + }; + + // need to recompute immediately in case start_offset != trigger_offset + completion.recompute_filter(editor); + + completion } - pub fn update(&mut self, cx: &mut commands::Context) { + pub fn recompute_filter(&mut self, editor: &Editor) { // recompute menu based on matches let menu = self.popup.contents_mut(); - let (view, doc) = current!(cx.editor); + let (view, doc) = current_ref!(editor); // cx.hooks() // cx.add_hook(enum type, ||) @@ -200,14 +209,18 @@ impl Completion { .selection(view.id) .primary() .cursor(doc.text().slice(..)); - if self.trigger_offset <= cursor { - let fragment = doc.text().slice(self.trigger_offset..cursor); + if self.start_offset <= cursor { + let fragment = doc.text().slice(self.start_offset..cursor); let text = Cow::from(fragment); // TODO: logic is same as ui/picker menu.score(&text); } } + pub fn update(&mut self, cx: &mut commands::Context) { + self.recompute_filter(cx.editor) + } + pub fn is_empty(&self) -> bool { self.popup.contents().is_empty() } diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index ee3b3c65..fda6a6d3 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -721,12 +721,15 @@ impl EditorView { pub fn set_completion( &mut self, + editor: &Editor, items: Vec, offset_encoding: helix_lsp::OffsetEncoding, + start_offset: usize, trigger_offset: usize, size: Rect, ) { - let mut completion = Completion::new(items, offset_encoding, trigger_offset); + let mut completion = + Completion::new(editor, items, offset_encoding, start_offset, trigger_offset); // TODO : propagate required size on resize to completion too completion.required_size((size.width, size.height)); self.completion = Some(completion); diff --git a/helix-view/src/macros.rs b/helix-view/src/macros.rs index c9a04270..0bebd02f 100644 --- a/helix-view/src/macros.rs +++ b/helix-view/src/macros.rs @@ -44,3 +44,19 @@ macro_rules! view { $( $editor ).+ .tree.get($( $editor ).+ .tree.focus) }}; } + +#[macro_export] +macro_rules! doc { + ( $( $editor:ident ).+ ) => {{ + $crate::current_ref!( $( $editor ).+ ).1 + }}; +} + +#[macro_export] +macro_rules! current_ref { + ( $( $editor:ident ).+ ) => {{ + let view = $( $editor ).+ .tree.get($( $editor ).+ .tree.focus); + let doc = &$( $editor ).+ .documents[view.doc]; + (view, doc) + }}; +} -- cgit v1.2.3-70-g09d2 From f692ede2b7af9ee3c0a83642c0e169ac2db90ca5 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Thu, 7 Oct 2021 10:37:35 +0900 Subject: fix: Don't crash on empty completion, don't retrigger on close --- helix-term/src/ui/editor.rs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'helix-term/src/ui') diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index fda6a6d3..9234bb96 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -730,6 +730,12 @@ impl EditorView { ) { let mut completion = Completion::new(editor, items, offset_encoding, start_offset, trigger_offset); + + if completion.is_empty() { + // skip if we got no completion results + return; + } + // TODO : propagate required size on resize to completion too completion.required_size((size.width, size.height)); self.completion = Some(completion); @@ -939,6 +945,7 @@ impl Component for EditorView { if callback.is_some() { // assume close_fn self.completion = None; + cxt.editor.clear_idle_timer(); // don't retrigger } } } @@ -952,6 +959,7 @@ impl Component for EditorView { completion.update(&mut cxt); if completion.is_empty() { self.completion = None; + cxt.editor.clear_idle_timer(); // don't retrigger } } } -- cgit v1.2.3-70-g09d2 From a6852fb88f72ea6bb41b19e65b531c17662ba57d Mon Sep 17 00:00:00 2001 From: Leoi Hung Kin Date: Sat, 9 Oct 2021 19:34:10 +0800 Subject: Picker: Don't panick at move_up/move_down when matches is empty (#818) --- helix-term/src/ui/picker.rs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'helix-term/src/ui') diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index c5b90a9c..9be2a73e 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -270,12 +270,18 @@ impl Picker { } pub fn move_up(&mut self) { + if self.matches.is_empty() { + return; + } let len = self.matches.len(); let pos = ((self.cursor + len.saturating_sub(1)) % len) % len; self.cursor = pos; } pub fn move_down(&mut self) { + if self.matches.is_empty() { + return; + } let len = self.matches.len(); let pos = (self.cursor + 1) % len; self.cursor = pos; -- cgit v1.2.3-70-g09d2 From 76b1bbc23ad5fc47765472cd9e83727a43c97ff3 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Sun, 10 Oct 2021 12:33:22 +0900 Subject: Allow trigger_offset to be unused for now --- helix-term/src/ui/completion.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'helix-term/src/ui') diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 72b03274..ba009c50 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -70,6 +70,7 @@ impl menu::Item for CompletionItem { pub struct Completion { popup: Popup>, start_offset: usize, + #[allow(dead_code)] trigger_offset: usize, // TODO: maintain a completioncontext with trigger kind & trigger char } -- cgit v1.2.3-70-g09d2 From 2c0468ffd16de1f835ac9c4e39692a682273fb7b Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Sat, 16 Oct 2021 18:43:07 +0900 Subject: fix: If backspacing past the start offset, cancel completion Refs #822 --- helix-term/src/ui/completion.rs | 4 ++++ helix-term/src/ui/menu.rs | 8 ++++++++ 2 files changed, 12 insertions(+) (limited to 'helix-term/src/ui') diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index ba009c50..c75b24f1 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -215,6 +215,10 @@ impl Completion { let text = Cow::from(fragment); // TODO: logic is same as ui/picker menu.score(&text); + } else { + // we backspaced before the start offset, clear the menu + // this will cause the editor to remove the completion popup + menu.clear(); } } diff --git a/helix-term/src/ui/menu.rs b/helix-term/src/ui/menu.rs index dab0c34f..055593fd 100644 --- a/helix-term/src/ui/menu.rs +++ b/helix-term/src/ui/menu.rs @@ -90,6 +90,14 @@ impl Menu { self.recalculate = true; } + pub fn clear(&mut self) { + self.matches.clear(); + + // reset cursor position + self.cursor = None; + self.scroll = 0; + } + pub fn move_up(&mut self) { let len = self.matches.len(); let pos = self.cursor.map_or(0, |i| (i + len.saturating_sub(1)) % len) % len; -- cgit v1.2.3-70-g09d2