Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-term/src/handlers/completion.rs')
| -rw-r--r-- | helix-term/src/handlers/completion.rs | 122 |
1 files changed, 66 insertions, 56 deletions
diff --git a/helix-term/src/handlers/completion.rs b/helix-term/src/handlers/completion.rs index 22bb0ce0..41ada7b4 100644 --- a/helix-term/src/handlers/completion.rs +++ b/helix-term/src/handlers/completion.rs @@ -1,14 +1,18 @@ use std::collections::HashMap; +use std::sync::Arc; + +use anyhow::Result; use helix_core::chars::char_is_word; use helix_core::completion::CompletionProvider; -use helix_core::syntax::config::LanguageServerFeature; -use helix_event::{register_hook, TaskHandle}; +use helix_core::syntax::LanguageServerFeature; +use helix_event::{register_hook, send_blocking, TaskHandle}; use helix_lsp::lsp; use helix_stdx::rope::RopeSliceExt; -use helix_view::document::Mode; -use helix_view::handlers::completion::{CompletionEvent, ResponseContext}; +use helix_view::document::{Mode, SavePoint}; +use helix_view::handlers::lsp::CompletionEvent; use helix_view::Editor; +use tokio::sync::mpsc::Sender; use tokio::task::JoinSet; use crate::commands; @@ -17,7 +21,7 @@ use crate::events::{OnModeSwitch, PostCommand, PostInsertChar}; use crate::handlers::completion::request::{request_incomplete_completion_list, Trigger}; use crate::job::dispatch; use crate::keymap::MappableCommand; -use crate::ui::lsp::signature_help::SignatureHelp; +use crate::ui::lsp::SignatureHelp; use crate::ui::{self, Popup}; use super::Handlers; @@ -30,15 +34,14 @@ mod item; mod path; mod request; mod resolve; -mod word; async fn handle_response( requests: &mut JoinSet<CompletionResponse>, - is_incomplete: bool, + incomplete: bool, ) -> Option<CompletionResponse> { loop { let response = requests.join_next().await?.unwrap(); - if !is_incomplete && !response.context.is_incomplete && response.items.is_empty() { + if !incomplete && !response.incomplete && response.items.is_empty() { continue; } return Some(response); @@ -48,9 +51,9 @@ async fn handle_response( async fn replace_completions( handle: TaskHandle, mut requests: JoinSet<CompletionResponse>, - is_incomplete: bool, + incomplete: bool, ) { - while let Some(mut response) = handle_response(&mut requests, is_incomplete).await { + while let Some(response) = handle_response(&mut requests, incomplete).await { let handle = handle.clone(); dispatch(move |editor, compositor| { let editor_view = compositor.find::<ui::EditorView>().unwrap(); @@ -58,22 +61,15 @@ async fn replace_completions( return; }; if handle.is_canceled() { - log::info!("dropping outdated completion response"); + log::error!("dropping outdated completion response"); return; } - - completion.replace_provider_completions(&mut response, is_incomplete); + completion.replace_provider_completions(response); if completion.is_empty() { editor_view.clear_completion(editor); - // clearing completions might mean we want to immediately re-request them (usually + // clearing completions might mean we want to immediately rerequest them (usually // this occurs if typing a trigger char) - trigger_auto_completion(editor, false); - } else { - editor - .handlers - .completions - .active_completions - .insert(response.provider, response.context); + trigger_auto_completion(&editor.handlers.completions, editor, false); } }) .await; @@ -83,9 +79,10 @@ async fn replace_completions( fn show_completion( editor: &mut Editor, compositor: &mut Compositor, - mut items: Vec<CompletionItem>, - context: HashMap<CompletionProvider, ResponseContext>, + items: Vec<CompletionItem>, + incomplete_completion_lists: HashMap<CompletionProvider, i8>, trigger: Trigger, + savepoint: Arc<SavePoint>, ) { let (view, doc) = current_ref!(editor); // check if the completion request is stale. @@ -102,10 +99,15 @@ fn show_completion( if ui.completion.is_some() { return; } - word::retain_valid_completions(trigger, doc, view.id, &mut items); - editor.handlers.completions.active_completions = context; - let completion_area = ui.set_completion(editor, items, trigger.pos, size); + let completion_area = ui.set_completion( + editor, + savepoint, + items, + incomplete_completion_lists, + trigger.pos, + size, + ); let signature_help_area = compositor .find_id::<Popup<SignatureHelp>>(SignatureHelp::ID) .map(|signature_help| signature_help.area(size, editor)); @@ -115,7 +117,11 @@ fn show_completion( } } -pub fn trigger_auto_completion(editor: &Editor, trigger_char_only: bool) { +pub fn trigger_auto_completion( + tx: &Sender<CompletionEvent>, + editor: &Editor, + trigger_char_only: bool, +) { let config = editor.config.load(); if !config.auto_completion { return; @@ -143,13 +149,15 @@ pub fn trigger_auto_completion(editor: &Editor, trigger_char_only: bool) { #[cfg(not(windows))] let is_path_completion_trigger = matches!(cursor_char, Some(b'/')); - let handler = &editor.handlers.completions; if is_trigger_char || (is_path_completion_trigger && doc.path_completion_enabled()) { - handler.event(CompletionEvent::TriggerChar { - cursor, - doc: doc.id(), - view: view.id, - }); + send_blocking( + tx, + CompletionEvent::TriggerChar { + cursor, + doc: doc.id(), + view: view.id, + }, + ); return; } @@ -162,29 +170,32 @@ pub fn trigger_auto_completion(editor: &Editor, trigger_char_only: bool) { .all(char_is_word); if is_auto_trigger { - handler.event(CompletionEvent::AutoTrigger { - cursor, - doc: doc.id(), - view: view.id, - }); + send_blocking( + tx, + CompletionEvent::AutoTrigger { + cursor, + doc: doc.id(), + view: view.id, + }, + ); } } fn update_completion_filter(cx: &mut commands::Context, c: Option<char>) { cx.callback.push(Box::new(move |compositor, cx| { let editor_view = compositor.find::<ui::EditorView>().unwrap(); - if let Some(completion) = &mut editor_view.completion { - completion.update_filter(c); - if completion.is_empty() || c.is_some_and(|c| !char_is_word(c)) { + if let Some(ui) = &mut editor_view.completion { + ui.update_filter(c); + if ui.is_empty() || c.is_some_and(|c| !char_is_word(c)) { editor_view.clear_completion(cx.editor); // clearing completions might mean we want to immediately rerequest them (usually // this occurs if typing a trigger char) if c.is_some() { - trigger_auto_completion(cx.editor, false); + trigger_auto_completion(&cx.editor.handlers.completions, cx.editor, false); } } else { - let handle = cx.editor.handlers.completions.request_controller.restart(); - request_incomplete_completion_list(cx.editor, handle) + let handle = ui.incomplete_list_controller.restart(); + request_incomplete_completion_list(cx.editor, ui, handle) } } })) @@ -198,8 +209,9 @@ fn clear_completions(cx: &mut commands::Context) { } fn completion_post_command_hook( + tx: &Sender<CompletionEvent>, PostCommand { command, cx }: &mut PostCommand<'_, '_>, -) -> anyhow::Result<()> { +) -> Result<()> { if cx.editor.mode == Mode::Insert { if cx.editor.last_completion.is_some() { match command { @@ -236,35 +248,33 @@ fn completion_post_command_hook( } => return Ok(()), _ => CompletionEvent::Cancel, }; - cx.editor.handlers.completions.event(event); + send_blocking(tx, event); } } Ok(()) } -pub(super) fn register_hooks(_handlers: &Handlers) { - register_hook!(move |event: &mut PostCommand<'_, '_>| completion_post_command_hook(event)); +pub(super) fn register_hooks(handlers: &Handlers) { + let tx = handlers.completions.clone(); + register_hook!(move |event: &mut PostCommand<'_, '_>| completion_post_command_hook(&tx, event)); + let tx = handlers.completions.clone(); register_hook!(move |event: &mut OnModeSwitch<'_, '_>| { if event.old_mode == Mode::Insert { - event - .cx - .editor - .handlers - .completions - .event(CompletionEvent::Cancel); + send_blocking(&tx, CompletionEvent::Cancel); clear_completions(event.cx); } else if event.new_mode == Mode::Insert { - trigger_auto_completion(event.cx.editor, false) + trigger_auto_completion(&tx, event.cx.editor, false) } Ok(()) }); + let tx = handlers.completions.clone(); register_hook!(move |event: &mut PostInsertChar<'_, '_>| { if event.cx.editor.last_completion.is_some() { update_completion_filter(event.cx, Some(event.c)) } else { - trigger_auto_completion(event.cx.editor, false); + trigger_auto_completion(&tx, event.cx.editor, false); } Ok(()) }); |