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.rs122
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(())
});