diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index ab268041b..79921268b 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 1dbb56168..3798c99a2 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 6c9e3a808..72b032741 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 ee3b3c65a..fda6a6d3a 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 c9a042701..0bebd02fc 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) + }}; +}