@@ -121,8 +121,9 @@ impl Completion {
121 include_placeholder: bool,
121 include_placeholder: bool,
122 ) -> Transaction {
122 ) -> Transaction {
123 use helix_lsp::snippet;
123 use helix_lsp::snippet;
124+ let selection = doc.selection(view_id);
125− if let Some(edit) = &item.text_edit {
126+ let (start_offset, end_offset, new_text) = if let Some(edit) = &item.text_edit {
126 let edit = match edit {
127 let edit = match edit {
127 lsp::CompletionTextEdit::Edit(edit) => edit.clone(),
128 lsp::CompletionTextEdit::Edit(edit) => edit.clone(),
128 lsp::CompletionTextEdit::InsertAndReplace(item) => {
129 lsp::CompletionTextEdit::InsertAndReplace(item) => {
@@ -130,46 +131,27 @@ impl Completion {
130 lsp::TextEdit::new(item.replace, item.new_text.clone())
131 lsp::TextEdit::new(item.replace, item.new_text.clone())
133−
134+ let text = doc.text().slice(..);
134− if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET))
135+ let primary_cursor = selection.primary().cursor(text);
136− item.insert_text_format,
137+ let start_offset =
137− Some(lsp::InsertTextFormat::SNIPPET)
140− match snippet::parse(&edit.new_text) {
141− Ok(snippet) => util::generate_transaction_from_snippet(
143− doc.selection(view_id),
146− doc.line_ending.as_str(),
147− include_placeholder,
148− offset_encoding,
138+ match util::lsp_pos_to_pos(doc.text(), edit.range.start, offset_encoding) {
149− ),
139+ Some(start) => start as i128 - primary_cursor as i128,
152− "Failed to parse snippet: {:?}, remaining output: {}",
156− Transaction::new(doc.text())
140+ None => return Transaction::new(doc.text()),
159− } else {
142+ let end_offset =
160− util::generate_transaction_from_completion_edit(
143+ match util::lsp_pos_to_pos(doc.text(), edit.range.end, offset_encoding) {
161− doc.text(),
144+ Some(end) => end as i128 - primary_cursor as i128,
162− doc.selection(view_id),
145+ None => return Transaction::new(doc.text()),
164− offset_encoding, // TODO: should probably transcode in Client
148+ (start_offset, end_offset, edit.new_text)
168− let text = item.insert_text.as_ref().unwrap_or(&item.label);
150+ let new_text = item.insert_text.as_ref().unwrap_or(&item.label);
169 // Some LSPs just give you an insertText with no offset ¯\_(ツ)_/¯
151 // Some LSPs just give you an insertText with no offset ¯\_(ツ)_/¯
170 // in these cases we need to check for a common prefix and remove it
152 // in these cases we need to check for a common prefix and remove it
171 let prefix = Cow::from(doc.text().slice(start_offset..trigger_offset));
153 let prefix = Cow::from(doc.text().slice(start_offset..trigger_offset));
172− let text = text.trim_start_matches::<&str>(&prefix);
154+ let new_text = new_text.trim_start_matches::<&str>(&prefix);
174 // TODO: this needs to be true for the numbers to work out correctly
156 // TODO: this needs to be true for the numbers to work out correctly
175 // in the closure below. It's passed in to a callback as this same
157 // in the closure below. It's passed in to a callback as this same
@@ -182,11 +164,42 @@ impl Completion {
182 == trigger_offset
164 == trigger_offset
185− Transaction::change_by_selection(doc.text(), doc.selection(view_id), |range| {
167+ (0, 0, new_text.into())
186− let cursor = range.cursor(doc.text().slice(..));
168+ };
188− (cursor, cursor, Some(text.into()))
170+ if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET))
172+ item.insert_text_format,
173+ Some(lsp::InsertTextFormat::SNIPPET)
176+ match snippet::parse(&new_text) {
177+ Ok(snippet) => util::generate_transaction_from_snippet(
183+ doc.line_ending.as_str(),
184+ include_placeholder,
188+ "Failed to parse snippet: {:?}, remaining output: {}",
192+ Transaction::new(doc.text())
196+ util::generate_transaction_from_completion_edit(