Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle is_incomplete #10822

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 93 additions & 35 deletions helix-term/src/handlers/completion.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::time::Duration;

Expand All @@ -9,8 +9,9 @@ use helix_core::syntax::LanguageServerFeature;
use helix_event::{
cancelable_future, cancelation, register_hook, send_blocking, CancelRx, CancelTx,
};
use helix_lsp::lsp;
use helix_lsp::lsp::{self, CompletionList};
use helix_lsp::util::pos_to_lsp_pos;
use helix_lsp::LanguageServerId;
use helix_stdx::rope::RopeSliceExt;
use helix_view::document::{Mode, SavePoint};
use helix_view::handlers::lsp::CompletionEvent;
Expand All @@ -27,7 +28,7 @@ use crate::job::{dispatch, dispatch_blocking};
use crate::keymap::MappableCommand;
use crate::ui::editor::InsertEvent;
use crate::ui::lsp::SignatureHelp;
use crate::ui::{self, CompletionItem, Popup};
use crate::ui::{self, CompletionDetails, CompletionItem, Popup};

use super::Handlers;
pub use resolve::ResolveHandler;
Expand Down Expand Up @@ -171,12 +172,16 @@ fn request_completion(
) {
let (view, doc) = current!(editor);

if compositor



let completion = &compositor
.find::<ui::EditorView>()
.unwrap()
.completion
.is_some()
|| editor.mode != Mode::Insert
.completion;

if completion.as_ref().is_some_and(|completion| completion.incomplete_ids().is_empty())
|| editor.mode != Mode::Insert
{
return;
}
Expand All @@ -197,10 +202,22 @@ fn request_completion(
trigger.pos = cursor;
let trigger_text = text.slice(..cursor);

let ls_filter: Box<dyn Fn(_) -> bool> = match completion {
None => Box::from(|_: LanguageServerId| true),
Some(completion) => {
let is_incomplete_ids = completion.incomplete_ids();

Box::from(move |ls: LanguageServerId| {
is_incomplete_ids.contains(&ls)
})
}
};

let mut seen_language_servers = HashSet::new();
let mut futures: FuturesUnordered<_> = doc
.language_servers_with_feature(LanguageServerFeature::Completion)
.filter(|ls| seen_language_servers.insert(ls.id()))
.filter(|ls| ls_filter(ls.id()))
.map(|ls| {
let language_server_id = ls.id();
let offset_encoding = ls.offset_encoding();
Expand Down Expand Up @@ -241,51 +258,52 @@ fn request_completion(
async move {
let json = completion_response.await?;
let response: Option<lsp::CompletionResponse> = serde_json::from_value(json)?;
let items = match response {
Some(lsp::CompletionResponse::Array(items)) => items,
// TODO: do something with is_incomplete
Some(lsp::CompletionResponse::List(lsp::CompletionList {
is_incomplete: _is_incomplete,
items,
})) => items,
None => Vec::new(),
}
.into_iter()
.map(|item| CompletionItem {
item,
provider: language_server_id,
resolved: false,
})
.collect();
anyhow::Ok(items)
let response = response
.map(|response| match response { // (completion items, (id, is_incomplete)) to be later collected into HashMap
lsp::CompletionResponse::Array(items) => (items, (language_server_id, CompletionDetails::default())),
lsp::CompletionResponse::List(CompletionList { is_incomplete, items }) => (items, (language_server_id, CompletionDetails {is_incomplete}))
})
.map(|(items, comp_type)| (
items.into_iter().map(|item| CompletionItem {item, provider: language_server_id, resolved: false}).collect::<Vec<CompletionItem>>(),
comp_type
));

anyhow::Ok(response)
}
})
.collect();

let future = async move {
let mut items = Vec::new();
while let Some(lsp_items) = futures.next().await {
match lsp_items {
Ok(mut lsp_items) => items.append(&mut lsp_items),
let mut cmp_is_incomplete: HashMap<LanguageServerId, CompletionDetails> = HashMap::new();

while let Some(response) = futures.next().await {
match response {
Ok(Some((mut lsp_items, lsp_type_pair))) => {
items.append(&mut lsp_items);
cmp_is_incomplete.insert(lsp_type_pair.0, lsp_type_pair.1);
},
Err(err) => {
log::debug!("completion request failed: {err:?}");
}
},
Ok(None) => (),
};
}
items
(items, cmp_is_incomplete)
};

let savepoint = doc.savepoint(view);

let ui = compositor.find::<ui::EditorView>().unwrap();
ui.last_insert.1.push(InsertEvent::RequestCompletion);
tokio::spawn(async move {
let items = cancelable_future(future, cancel).await.unwrap_or_default();
let (items, lsp_cmp_details) = cancelable_future(future, cancel).await.unwrap_or_default();

if items.is_empty() {
return;
}
dispatch(move |editor, compositor| {
show_completion(editor, compositor, items, trigger, savepoint)
show_completion(editor, compositor, items, lsp_cmp_details, trigger, savepoint)
})
.await
});
Expand All @@ -295,6 +313,7 @@ fn show_completion(
editor: &mut Editor,
compositor: &mut Compositor,
items: Vec<CompletionItem>,
lsp_cmp_details: HashMap<LanguageServerId, CompletionDetails>,
trigger: Trigger,
savepoint: Arc<SavePoint>,
) {
Expand All @@ -310,11 +329,39 @@ fn show_completion(

let size = compositor.size();
let ui = compositor.find::<ui::EditorView>().unwrap();
if ui.completion.is_some() {
return;
}

// Persist old completions and completion window offset on is_incomplete
let completion_area = match &ui.completion {
Some(completion) => {
let offset = completion.trigger_offset();

println!("offset: {offset}");

let complete_items = completion.complete_items();

let all_items = complete_items
.map(|item| item.clone()) // TODO: Workaround
.chain(items.into_iter())
.collect::<Vec<_>>();

// TODO: how to align the new completion menu with the old one? I am trying to set the offset but
// it is not working
let area = ui.set_completion(editor, savepoint, all_items, lsp_cmp_details, offset, size);


// TODO: do we need to rerank? and Would the completion menu change?
// if let Some(completion) = &compositor.find::<ui::EditorView>().unwrap().completion {
// completion.rerank
// }

area



},
None => ui.set_completion(editor, savepoint, items, lsp_cmp_details, trigger.pos, size)
};

let completion_area = ui.set_completion(editor, savepoint, items, trigger.pos, size);
let signature_help_area = compositor
.find_id::<Popup<SignatureHelp>>(SignatureHelp::ID)
.map(|signature_help| signature_help.area(size, editor));
Expand Down Expand Up @@ -383,6 +430,17 @@ fn update_completions(cx: &mut commands::Context, c: Option<char>) {
let editor_view = compositor.find::<ui::EditorView>().unwrap();
if let Some(completion) = &mut editor_view.completion {
completion.update_filter(c);


// Handle completions with is_incomplete
let ids = completion.incomplete_ids();
if !ids.is_empty() {

trigger_auto_completion(&cx.editor.handlers.completions, cx.editor, false);

}


if completion.is_empty() {
editor_view.clear_completion(cx.editor);
// clearing completions might mean we want to immediately rerequest them (usually
Expand Down
41 changes: 40 additions & 1 deletion helix-term/src/ui/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use helix_view::{
};
use tui::{buffer::Buffer as Surface, text::Span};

use std::{borrow::Cow, sync::Arc};
use std::{borrow::Cow, collections::{HashMap, HashSet}, sync::Arc};

use helix_core::{chars, Change, Transaction};
use helix_view::{graphics::Rect, Document, Editor};
Expand Down Expand Up @@ -94,13 +94,27 @@ pub struct CompletionItem {
pub resolved: bool,
}

#[derive(Debug, PartialEq, Clone)]
pub struct CompletionDetails {
pub is_incomplete: bool
}

impl Default for CompletionDetails {
fn default() -> Self {
Self {
is_incomplete: false
}
}
}

/// Wraps a Menu.
pub struct Completion {
popup: Popup<Menu<CompletionItem>>,
#[allow(dead_code)]
trigger_offset: usize,
filter: String,
resolve_handler: ResolveHandler,
lsp_cmp_details: HashMap<LanguageServerId, CompletionDetails>
}

impl Completion {
Expand All @@ -110,6 +124,7 @@ impl Completion {
editor: &Editor,
savepoint: Arc<SavePoint>,
mut items: Vec<CompletionItem>,
lsp_cmp_details: HashMap<LanguageServerId, CompletionDetails>,
trigger_offset: usize,
) -> Self {
let preview_completion_insert = editor.config().preview_completion_insert;
Expand Down Expand Up @@ -351,6 +366,8 @@ impl Completion {
let mut completion = Self {
popup,
trigger_offset,
lsp_cmp_details,

// TODO: expand nucleo api to allow moving straight to a Utf32String here
// and avoid allocation during matching
filter: String::from(fragment),
Expand Down Expand Up @@ -421,6 +438,28 @@ impl Completion {
pub fn area(&mut self, viewport: Rect, editor: &Editor) -> Rect {
self.popup.area(viewport, editor)
}

pub fn incomplete_ids(&self) -> HashSet<LanguageServerId> {
self.lsp_cmp_details.iter()
.flat_map(|(&id, details)| match details {
CompletionDetails {is_incomplete: true} => Some(id),
_ => None
})
.collect()
}

pub fn trigger_offset(&self) -> usize {
self.trigger_offset
}

pub fn complete_items(&self) -> impl Iterator<Item = &CompletionItem> {
let incomplete_ids = self.incomplete_ids();

self.popup.contents().items()
.iter()
.filter(move |item| !incomplete_ids.contains(&item.provider))

}
}

impl Component for Completion {
Expand Down
8 changes: 5 additions & 3 deletions helix-term/src/ui/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use helix_core::{
unicode::width::UnicodeWidthStr,
visual_offset_from_block, Change, Position, Range, Selection, Transaction,
};
use helix_lsp::LanguageServerId;
use helix_view::{
document::{Mode, SavePoint, SCRATCH_BUFFER_NAME},
editor::{CompleteAction, CursorShapeConfig},
Expand All @@ -27,11 +28,11 @@ use helix_view::{
keyboard::{KeyCode, KeyModifiers},
Document, Editor, Theme, View,
};
use std::{mem::take, num::NonZeroUsize, path::PathBuf, rc::Rc, sync::Arc};
use std::{collections::HashMap, mem::take, num::NonZeroUsize, path::PathBuf, rc::Rc, sync::Arc};

use tui::{buffer::Buffer as Surface, text::Span};

use super::document::LineDecoration;
use super::{completion::CompletionDetails, document::LineDecoration};
use super::{completion::CompletionItem, statusline};

pub struct EditorView {
Expand Down Expand Up @@ -1019,10 +1020,11 @@ impl EditorView {
editor: &mut Editor,
savepoint: Arc<SavePoint>,
items: Vec<CompletionItem>,
lsp_cmp_details: HashMap<LanguageServerId, CompletionDetails>,
trigger_offset: usize,
size: Rect,
) -> Option<Rect> {
let mut completion = Completion::new(editor, savepoint, items, trigger_offset);
let mut completion = Completion::new(editor, savepoint, items, lsp_cmp_details, trigger_offset);

if completion.is_empty() {
// skip if we got no completion results
Expand Down
4 changes: 4 additions & 0 deletions helix-term/src/ui/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ impl<T: Item> Menu<T> {
pub fn len(&self) -> usize {
self.matches.len()
}

pub fn items(&self) -> &Vec<T> {
&self.options
}
}

impl<T: Item + PartialEq> Menu<T> {
Expand Down
2 changes: 1 addition & 1 deletion helix-term/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mod text;
use crate::compositor::Compositor;
use crate::filter_picker_entry;
use crate::job::{self, Callback};
pub use completion::{Completion, CompletionItem};
pub use completion::{Completion, CompletionItem, CompletionDetails};
pub use editor::EditorView;
use helix_stdx::rope;
pub use markdown::Markdown;
Expand Down
2 changes: 1 addition & 1 deletion helix-view/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,7 @@ impl Default for Config {
idle_timeout: Duration::from_millis(250),
completion_timeout: Duration::from_millis(250),
preview_completion_insert: true,
completion_trigger_len: 2,
completion_trigger_len: 1,
auto_info: true,
file_picker: FilePickerConfig::default(),
statusline: StatusLineConfig::default(),
Expand Down
Loading