Skip to content

Commit d8bbfe7

Browse files
committed
Added support for merging completion results of multiple language servers
1 parent a705b70 commit d8bbfe7

File tree

3 files changed

+87
-54
lines changed

3 files changed

+87
-54
lines changed

helix-term/src/commands.rs

Lines changed: 55 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3611,23 +3611,8 @@ pub fn completion(cx: &mut Context) {
36113611

36123612
let (view, doc) = current!(cx.editor);
36133613

3614-
// TODO merge completion items of multiple language servers,
3615-
// instead of taking the first language server for completion
3616-
let language_server = match doc.language_servers().first() {
3617-
Some(language_server) => *language_server,
3618-
None => return,
3619-
};
3620-
3621-
let language_server_id = language_server.id();
3622-
3623-
let offset_encoding = language_server.offset_encoding();
36243614
let text = doc.text().slice(..);
36253615
let cursor = doc.selection(view.id).primary().cursor(text);
3626-
3627-
let pos = pos_to_lsp_pos(doc.text(), cursor, offset_encoding);
3628-
3629-
let future = language_server.completion(doc.identifier(), pos, None);
3630-
36313616
let trigger_offset = cursor;
36323617

36333618
// TODO: trigger_offset should be the cursor offset but we also need a starting offset from where we want to apply
@@ -3640,48 +3625,64 @@ pub fn completion(cx: &mut Context) {
36403625
let start_offset = cursor.saturating_sub(offset);
36413626
let prefix = text.slice(start_offset..cursor).to_string();
36423627

3643-
cx.callback(
3644-
future,
3645-
move |editor, compositor, response: Option<lsp::CompletionResponse>| {
3646-
let doc = doc!(editor);
3647-
if doc.mode() != Mode::Insert {
3648-
// we're not in insert mode anymore
3649-
return;
3650-
}
3628+
let mut requests = Vec::new();
36513629

3652-
let mut items = match response {
3653-
Some(lsp::CompletionResponse::Array(items)) => items,
3654-
// TODO: do something with is_incomplete
3655-
Some(lsp::CompletionResponse::List(lsp::CompletionList {
3656-
is_incomplete: _is_incomplete,
3657-
items,
3658-
})) => items,
3659-
None => Vec::new(),
3660-
}
3661-
.into_iter()
3662-
.map(|item| CompletionItem::LSP {
3663-
language_server_id,
3664-
item,
3665-
offset_encoding,
3666-
})
3667-
.collect::<Vec<_>>();
3630+
for language_server in doc.language_servers() {
3631+
let language_server_id = language_server.id();
36683632

3669-
if !prefix.is_empty() {
3670-
items = items
3671-
.into_iter()
3672-
.filter(|item| item.filter_text().starts_with(&prefix))
3673-
.collect();
3674-
}
3633+
let offset_encoding = language_server.offset_encoding();
36753634

3676-
if items.is_empty() {
3677-
// editor.set_error("No completion available");
3678-
return;
3679-
}
3680-
let size = compositor.size();
3681-
let ui = compositor.find::<ui::EditorView>().unwrap();
3682-
ui.set_completion(editor, items, start_offset, trigger_offset, size);
3683-
},
3684-
);
3635+
let pos = pos_to_lsp_pos(doc.text(), cursor, offset_encoding);
3636+
3637+
let future = language_server.completion(doc.identifier(), pos, None);
3638+
requests.push((future, language_server_id, offset_encoding));
3639+
}
3640+
3641+
for (future, language_server_id, offset_encoding) in requests {
3642+
let prefix = prefix.clone();
3643+
cx.callback(
3644+
future,
3645+
move |editor, compositor, response: Option<lsp::CompletionResponse>| {
3646+
let doc = doc!(editor);
3647+
if doc.mode() != Mode::Insert {
3648+
// we're not in insert mode anymore
3649+
return;
3650+
}
3651+
3652+
let mut items = match response {
3653+
Some(lsp::CompletionResponse::Array(items)) => items,
3654+
// TODO: do something with is_incomplete
3655+
Some(lsp::CompletionResponse::List(lsp::CompletionList {
3656+
is_incomplete: _is_incomplete,
3657+
items,
3658+
})) => items,
3659+
None => Vec::new(),
3660+
}
3661+
.into_iter()
3662+
.map(|item| CompletionItem::LSP {
3663+
language_server_id,
3664+
item,
3665+
offset_encoding,
3666+
})
3667+
.collect::<Vec<_>>();
3668+
3669+
if !prefix.is_empty() {
3670+
items = items
3671+
.into_iter()
3672+
.filter(|item| item.filter_text().starts_with(&prefix))
3673+
.collect();
3674+
}
3675+
3676+
if items.is_empty() {
3677+
// editor.set_error("No completion available");
3678+
return;
3679+
}
3680+
let size = compositor.size();
3681+
let ui = compositor.find::<ui::EditorView>().unwrap();
3682+
ui.set_or_extend_completion(editor, items, start_offset, trigger_offset, size);
3683+
},
3684+
);
3685+
}
36853686
}
36863687

36873688
// comments

helix-term/src/ui/completion.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,14 @@ impl Completion {
271271
}
272272
}
273273
}
274+
275+
pub fn trigger_offset(&self) -> usize {
276+
self.trigger_offset
277+
}
278+
279+
pub fn start_offset(&self) -> usize {
280+
self.start_offset
281+
}
274282

275283
pub fn add_completion_items(&mut self, items: Vec<CompletionItem>) {
276284
self.popup.contents_mut().add_options(items);

helix-term/src/ui/editor.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,30 @@ impl EditorView {
937937
self.completion = Some(completion);
938938
}
939939

940+
pub fn set_or_extend_completion(
941+
&mut self,
942+
editor: &mut Editor,
943+
items: Vec<CompletionItem>,
944+
start_offset: usize,
945+
trigger_offset: usize,
946+
size: Rect,
947+
) {
948+
match &mut self.completion {
949+
Some(completion) => {
950+
// cheap check, if the completion menu resulted of the same 'completion' trigger (e.g. by commands::completion)
951+
// TODO test/check if this is enough/safe...
952+
if start_offset == completion.start_offset()
953+
&& completion.trigger_offset() == trigger_offset
954+
{
955+
completion.add_completion_items(items)
956+
} else {
957+
self.set_completion(editor, items, start_offset, trigger_offset, size)
958+
}
959+
}
960+
None => self.set_completion(editor, items, start_offset, trigger_offset, size),
961+
}
962+
}
963+
940964
pub fn clear_completion(&mut self, editor: &mut Editor) {
941965
self.completion = None;
942966

0 commit comments

Comments
 (0)