@@ -121,8 +121,9 @@ impl Completion {
121121 include_placeholder: bool,
122122 ) -> Transaction {
123123 use helix_lsp::snippet;
124+ let selection = doc.selection(view_id);
124125
125− if let Some(edit) = &item.text_edit {
126+ let (start_offset, end_offset, new_text) = if let Some(edit) = &item.text_edit {
126127 let edit = match edit {
127128 lsp::CompletionTextEdit::Edit(edit) => edit.clone(),
128129 lsp::CompletionTextEdit::InsertAndReplace(item) => {
@@ -130,46 +131,27 @@ impl Completion {
130131 lsp::TextEdit::new(item.replace, item.new_text.clone())
131132 }
132133 };
133−
134− if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET))
135− || matches!(
136− item.insert_text_format,
137− Some(lsp::InsertTextFormat::SNIPPET)
138− )
139− {
140− match snippet::parse(&edit.new_text) {
141− Ok(snippet) => util::generate_transaction_from_snippet(
142− doc.text(),
143− doc.selection(view_id),
144− &edit.range,
145− snippet,
146− doc.line_ending.as_str(),
147− include_placeholder,
148− offset_encoding,
149− ),
150− Err(err) => {
151− log::error!(
152− "Failed to parse snippet: {:?}, remaining output: {}",
153− &edit.new_text,
154− err
155− );
156− Transaction::new(doc.text())
157− }
158− }
159− } else {
160− util::generate_transaction_from_completion_edit(
161− doc.text(),
162− doc.selection(view_id),
163− edit,
164− offset_encoding, // TODO: should probably transcode in Client
165− )
166− }
134+ let text = doc.text().slice(..);
135+ let primary_cursor = selection.primary().cursor(text);
136+
137+ let start_offset =
138+ match util::lsp_pos_to_pos(doc.text(), edit.range.start, offset_encoding) {
139+ Some(start) => start as i128 - primary_cursor as i128,
140+ None => return Transaction::new(doc.text()),
141+ };
142+ let end_offset =
143+ match util::lsp_pos_to_pos(doc.text(), edit.range.end, offset_encoding) {
144+ Some(end) => end as i128 - primary_cursor as i128,
145+ None => return Transaction::new(doc.text()),
146+ };
147+
148+ (start_offset, end_offset, edit.new_text)
167149 } else {
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);
169151 // Some LSPs just give you an insertText with no offset ¯\_(ツ)_/¯
170152 // in these cases we need to check for a common prefix and remove it
171153 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);
173155
174156 // TODO: this needs to be true for the numbers to work out correctly
175157 // in the closure below. It's passed in to a callback as this same
@@ -182,11 +164,42 @@ impl Completion {
182164 == trigger_offset
183165 );
184166
185− Transaction::change_by_selection(doc.text(), doc.selection(view_id), |range| {
186− let cursor = range.cursor(doc.text().slice(..));
167+ (0, 0, new_text.into())
168+ };
187169
188− (cursor, cursor, Some(text.into()))
189− })
170+ if matches!(item.kind, Some(lsp::CompletionItemKind::SNIPPET))
171+ || matches!(
172+ item.insert_text_format,
173+ Some(lsp::InsertTextFormat::SNIPPET)
174+ )
175+ {
176+ match snippet::parse(&new_text) {
177+ Ok(snippet) => util::generate_transaction_from_snippet(
178+ doc.text(),
179+ selection,
180+ start_offset,
181+ end_offset,
182+ snippet,
183+ doc.line_ending.as_str(),
184+ include_placeholder,
185+ ),
186+ Err(err) => {
187+ log::error!(
188+ "Failed to parse snippet: {:?}, remaining output: {}",
189+ &new_text,
190+ err
191+ );
192+ Transaction::new(doc.text())
193+ }
194+ }
195+ } else {
196+ util::generate_transaction_from_completion_edit(
197+ doc.text(),
198+ selection,
199+ start_offset,
200+ end_offset,
201+ new_text,
202+ )
190203 }
191204 }
192205