@@ -5,7 +5,7 @@ use tui::buffer::Buffer as Surface;
5use std::borrow::Cow;
5use std::borrow::Cow;
7use helix_core::Transaction;
7use helix_core::Transaction;
8−use helix_view::{graphics::Rect, Document, Editor, View};
8+use helix_view::{graphics::Rect, Document, Editor};
10use crate::commands;
10use crate::commands;
11use crate::ui::{menu, Markdown, Menu, Popup, PromptEvent};
11use crate::ui::{menu, Markdown, Menu, Popup, PromptEvent};
@@ -83,13 +83,13 @@ impl Completion {
83 start_offset: usize,
83 start_offset: usize,
84 trigger_offset: usize,
84 trigger_offset: usize,
85 ) -> Self {
85 ) -> Self {
86− // let items: Vec<CompletionItem> = Vec::new();
87 let menu = Menu::new(items, move |editor: &mut Editor, item, event| {
86 let menu = Menu::new(items, move |editor: &mut Editor, item, event| {
88 fn item_to_transaction(
87 fn item_to_transaction(
89 doc: &Document,
88 doc: &Document,
91 item: &CompletionItem,
89 item: &CompletionItem,
92 offset_encoding: helix_lsp::OffsetEncoding,
90 offset_encoding: helix_lsp::OffsetEncoding,
92+ trigger_offset: usize,
93 ) -> Transaction {
93 ) -> Transaction {
94 if let Some(edit) = &item.text_edit {
94 if let Some(edit) = &item.text_edit {
95 let edit = match edit {
95 let edit = match edit {
@@ -105,63 +105,52 @@ impl Completion {
107 let text = item.insert_text.as_ref().unwrap_or(&item.label);
107 let text = item.insert_text.as_ref().unwrap_or(&item.label);
108− let cursor = doc
108+ // Some LSPs just give you an insertText with no offset ¯\_(ツ)_/¯
109− .selection(view.id)
109+ // in these cases we need to check for a common prefix and remove it
110− .primary()
110+ let prefix = Cow::from(doc.text().slice(start_offset..trigger_offset));
111− .cursor(doc.text().slice(..));
111+ let text = text.trim_start_matches::<&str>(&prefix);
112 Transaction::change(
112 Transaction::change(
113 doc.text(),
113 doc.text(),
114− vec![(cursor, cursor, Some(text.as_str().into()))].into_iter(),
114+ vec![(trigger_offset, trigger_offset, Some(text.into()))].into_iter(),
119+ let (view, doc) = current!(editor);
121+ // if more text was entered, remove it
122+ doc.restore(view.id);
119 match event {
124 match event {
120 PromptEvent::Abort => {}
125 PromptEvent::Abort => {}
121 PromptEvent::Update => {
126 PromptEvent::Update => {
122− let (view, doc) = current!(editor);
124 // always present here
127 // always present here
125 let item = item.unwrap();
128 let item = item.unwrap();
127− // if more text was entered, remove it
130+ let transaction = item_to_transaction(
128− // TODO: ideally to undo we should keep the last completion tx revert, and map it over new changes
131+ doc,
129− let cursor = doc
132+ item,
130− .selection(view.id)
133+ offset_encoding,
131− .primary()
134+ start_offset,
132− .cursor(doc.text().slice(..));
133− if trigger_offset < cursor {
134− let remove = Transaction::change(
136− vec![(trigger_offset, cursor, None)].into_iter(),
135+ trigger_offset,
138− doc.apply(&remove, view.id);
137+
139− }
138+ // initialize a savepoint
141− let transaction = item_to_transaction(doc, view, item, offset_encoding);
142 doc.apply(&transaction, view.id);
141 doc.apply(&transaction, view.id);
144 PromptEvent::Validate => {
143 PromptEvent::Validate => {
145− let (view, doc) = current!(editor);
147 // always present here
144 // always present here
148 let item = item.unwrap();
145 let item = item.unwrap();
150− // if more text was entered, remove it
151− // TODO: ideally to undo we should keep the last completion tx revert, and map it over new changes
155− .cursor(doc.text().slice(..));
156− if trigger_offset < cursor {
157− let remove = Transaction::change(
159− vec![(trigger_offset, cursor, None)].into_iter(),
161− doc.apply(&remove, view.id);
164− let transaction = item_to_transaction(doc, view, item, offset_encoding);
147+ let transaction = item_to_transaction(
165 doc.apply(&transaction, view.id);
154 doc.apply(&transaction, view.id);
167 if let Some(additional_edits) = &item.additional_text_edits {
156 if let Some(additional_edits) = &item.additional_text_edits {