aboutsummaryrefslogtreecommitdiff
path: root/helix-term/src/ui/completion.rs
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term/src/ui/completion.rs')
-rw-r--r--helix-term/src/ui/completion.rs134
1 files changed, 61 insertions, 73 deletions
diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs
index c75b24f1..dd782d29 100644
--- a/helix-term/src/ui/completion.rs
+++ b/helix-term/src/ui/completion.rs
@@ -5,7 +5,7 @@ use tui::buffer::Buffer as Surface;
use std::borrow::Cow;
use helix_core::Transaction;
-use helix_view::{graphics::Rect, Document, Editor, View};
+use helix_view::{graphics::Rect, Document, Editor};
use crate::commands;
use crate::ui::{menu, Markdown, Menu, Popup, PromptEvent};
@@ -30,31 +30,32 @@ impl menu::Item for CompletionItem {
menu::Row::new(vec![
menu::Cell::from(self.label.as_str()),
menu::Cell::from(match self.kind {
- Some(lsp::CompletionItemKind::Text) => "text",
- Some(lsp::CompletionItemKind::Method) => "method",
- Some(lsp::CompletionItemKind::Function) => "function",
- Some(lsp::CompletionItemKind::Constructor) => "constructor",
- Some(lsp::CompletionItemKind::Field) => "field",
- Some(lsp::CompletionItemKind::Variable) => "variable",
- Some(lsp::CompletionItemKind::Class) => "class",
- Some(lsp::CompletionItemKind::Interface) => "interface",
- Some(lsp::CompletionItemKind::Module) => "module",
- Some(lsp::CompletionItemKind::Property) => "property",
- Some(lsp::CompletionItemKind::Unit) => "unit",
- Some(lsp::CompletionItemKind::Value) => "value",
- Some(lsp::CompletionItemKind::Enum) => "enum",
- Some(lsp::CompletionItemKind::Keyword) => "keyword",
- Some(lsp::CompletionItemKind::Snippet) => "snippet",
- Some(lsp::CompletionItemKind::Color) => "color",
- Some(lsp::CompletionItemKind::File) => "file",
- Some(lsp::CompletionItemKind::Reference) => "reference",
- Some(lsp::CompletionItemKind::Folder) => "folder",
- Some(lsp::CompletionItemKind::EnumMember) => "enum_member",
- Some(lsp::CompletionItemKind::Constant) => "constant",
- Some(lsp::CompletionItemKind::Struct) => "struct",
- Some(lsp::CompletionItemKind::Event) => "event",
- Some(lsp::CompletionItemKind::Operator) => "operator",
- Some(lsp::CompletionItemKind::TypeParameter) => "type_param",
+ Some(lsp::CompletionItemKind::TEXT) => "text",
+ Some(lsp::CompletionItemKind::METHOD) => "method",
+ Some(lsp::CompletionItemKind::FUNCTION) => "function",
+ Some(lsp::CompletionItemKind::CONSTRUCTOR) => "constructor",
+ Some(lsp::CompletionItemKind::FIELD) => "field",
+ Some(lsp::CompletionItemKind::VARIABLE) => "variable",
+ Some(lsp::CompletionItemKind::CLASS) => "class",
+ Some(lsp::CompletionItemKind::INTERFACE) => "interface",
+ Some(lsp::CompletionItemKind::MODULE) => "module",
+ Some(lsp::CompletionItemKind::PROPERTY) => "property",
+ Some(lsp::CompletionItemKind::UNIT) => "unit",
+ Some(lsp::CompletionItemKind::VALUE) => "value",
+ Some(lsp::CompletionItemKind::ENUM) => "enum",
+ Some(lsp::CompletionItemKind::KEYWORD) => "keyword",
+ Some(lsp::CompletionItemKind::SNIPPET) => "snippet",
+ Some(lsp::CompletionItemKind::COLOR) => "color",
+ Some(lsp::CompletionItemKind::FILE) => "file",
+ Some(lsp::CompletionItemKind::REFERENCE) => "reference",
+ Some(lsp::CompletionItemKind::FOLDER) => "folder",
+ Some(lsp::CompletionItemKind::ENUM_MEMBER) => "enum_member",
+ Some(lsp::CompletionItemKind::CONSTANT) => "constant",
+ Some(lsp::CompletionItemKind::STRUCT) => "struct",
+ Some(lsp::CompletionItemKind::EVENT) => "event",
+ Some(lsp::CompletionItemKind::OPERATOR) => "operator",
+ Some(lsp::CompletionItemKind::TYPE_PARAMETER) => "type_param",
+ Some(kind) => unimplemented!("{:?}", kind),
None => "",
}),
// self.detail.as_deref().unwrap_or("")
@@ -83,13 +84,13 @@ impl Completion {
start_offset: usize,
trigger_offset: usize,
) -> Self {
- // let items: Vec<CompletionItem> = Vec::new();
let menu = Menu::new(items, move |editor: &mut Editor, item, event| {
fn item_to_transaction(
doc: &Document,
- view: &View,
item: &CompletionItem,
offset_encoding: helix_lsp::OffsetEncoding,
+ start_offset: usize,
+ trigger_offset: usize,
) -> Transaction {
if let Some(edit) = &item.text_edit {
let edit = match edit {
@@ -105,63 +106,52 @@ impl Completion {
)
} else {
let text = item.insert_text.as_ref().unwrap_or(&item.label);
- let cursor = doc
- .selection(view.id)
- .primary()
- .cursor(doc.text().slice(..));
+ // Some LSPs just give you an insertText with no offset ¯\_(ツ)_/¯
+ // in these cases we need to check for a common prefix and remove it
+ let prefix = Cow::from(doc.text().slice(start_offset..trigger_offset));
+ let text = text.trim_start_matches::<&str>(&prefix);
Transaction::change(
doc.text(),
- vec![(cursor, cursor, Some(text.as_str().into()))].into_iter(),
+ vec![(trigger_offset, trigger_offset, Some(text.into()))].into_iter(),
)
}
}
+ let (view, doc) = current!(editor);
+
+ // if more text was entered, remove it
+ doc.restore(view.id);
+
match event {
PromptEvent::Abort => {}
PromptEvent::Update => {
- let (view, doc) = current!(editor);
-
// always present here
let item = item.unwrap();
- // if more text was entered, remove it
- // TODO: ideally to undo we should keep the last completion tx revert, and map it over new changes
- let cursor = doc
- .selection(view.id)
- .primary()
- .cursor(doc.text().slice(..));
- if trigger_offset < cursor {
- let remove = Transaction::change(
- doc.text(),
- vec![(trigger_offset, cursor, None)].into_iter(),
- );
- doc.apply(&remove, view.id);
- }
+ let transaction = item_to_transaction(
+ doc,
+ item,
+ offset_encoding,
+ start_offset,
+ trigger_offset,
+ );
+
+ // initialize a savepoint
+ doc.savepoint();
- let transaction = item_to_transaction(doc, view, item, offset_encoding);
doc.apply(&transaction, view.id);
}
PromptEvent::Validate => {
- let (view, doc) = current!(editor);
-
// always present here
let item = item.unwrap();
- // if more text was entered, remove it
- // TODO: ideally to undo we should keep the last completion tx revert, and map it over new changes
- let cursor = doc
- .selection(view.id)
- .primary()
- .cursor(doc.text().slice(..));
- if trigger_offset < cursor {
- let remove = Transaction::change(
- doc.text(),
- vec![(trigger_offset, cursor, None)].into_iter(),
- );
- doc.apply(&remove, view.id);
- }
-
- let transaction = item_to_transaction(doc, view, item, offset_encoding);
+ let transaction = item_to_transaction(
+ doc,
+ item,
+ offset_encoding,
+ start_offset,
+ trigger_offset,
+ );
doc.apply(&transaction, view.id);
if let Some(additional_edits) = &item.additional_text_edits {
@@ -210,7 +200,7 @@ impl Completion {
.selection(view.id)
.primary()
.cursor(doc.text().slice(..));
- if self.start_offset <= cursor {
+ if self.trigger_offset <= cursor {
let fragment = doc.text().slice(self.start_offset..cursor);
let text = Cow::from(fragment);
// TODO: logic is same as ui/picker
@@ -274,12 +264,10 @@ impl Component for Completion {
.language()
.and_then(|scope| scope.strip_prefix("source."))
.unwrap_or("");
- let cursor_pos = doc
- .selection(view.id)
- .primary()
- .cursor(doc.text().slice(..));
- let cursor_pos = (helix_core::coords_at_pos(doc.text().slice(..), cursor_pos).row
- - view.offset.row) as u16;
+ let text = doc.text().slice(..);
+ let cursor_pos = doc.selection(view.id).primary().cursor(text);
+ let coords = helix_core::visual_coords_at_pos(text, cursor_pos, doc.tab_width());
+ let cursor_pos = (coords.row - view.offset.row) as u16;
let mut markdown_doc = match &option.documentation {
Some(lsp::Documentation::String(contents))
| Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {