Correctly handle multiple cursors with LSP snippets

pull/5465/merge
Urgau 2 years ago committed by Blaž Hrastnik
parent ba24cfe912
commit ec6e575a40

@ -578,6 +578,16 @@ impl Selection {
self.normalize() self.normalize()
} }
/// Takes a closure and maps each `Range` over the closure to multiple `Range`s.
pub fn transform_iter<F, I>(mut self, f: F) -> Self
where
F: FnMut(Range) -> I,
I: Iterator<Item = Range>,
{
self.ranges = self.ranges.into_iter().flat_map(f).collect();
self.normalize()
}
// Ensures the selection adheres to the following invariants: // Ensures the selection adheres to the following invariants:
// 1. All ranges are grapheme aligned. // 1. All ranges are grapheme aligned.
// 2. All ranges are at least 1 character wide, unless at the // 2. All ranges are at least 1 character wide, unless at the

@ -66,7 +66,7 @@ pub fn into_transaction<'a>(
offset_encoding: OffsetEncoding, offset_encoding: OffsetEncoding,
include_placeholer: bool, include_placeholer: bool,
) -> helix_core::Transaction { ) -> helix_core::Transaction {
use helix_core::{smallvec, Range, Selection, Transaction}; use helix_core::{smallvec, Range, Transaction};
use SnippetElement::*; use SnippetElement::*;
let text = doc.slice(..); let text = doc.slice(..);
@ -87,9 +87,9 @@ pub fn into_transaction<'a>(
blank = "" blank = ""
); );
let mut offset = 0;
let mut insert = String::new(); let mut insert = String::new();
let mut offset = (primary_cursor as i128 + start_offset) as usize; let mut tabstops: Vec<(usize, usize, usize)> = Vec::new();
let mut tabstops: Vec<(usize, Range)> = Vec::new();
for element in snippet.elements { for element in snippet.elements {
match element { match element {
@ -114,7 +114,7 @@ pub fn into_transaction<'a>(
insert.push_str(text); insert.push_str(text);
} }
Tabstop { tabstop } => { Tabstop { tabstop } => {
tabstops.push((tabstop, Range::point(offset))); tabstops.push((tabstop, offset, offset));
} }
Placeholder { tabstop, value } => match value.as_ref() { Placeholder { tabstop, value } => match value.as_ref() {
// https://doc.rust-lang.org/beta/unstable-book/language-features/box-patterns.html // https://doc.rust-lang.org/beta/unstable-book/language-features/box-patterns.html
@ -122,11 +122,11 @@ pub fn into_transaction<'a>(
Text(text) => { Text(text) => {
if include_placeholer { if include_placeholer {
let len_chars = text.chars().count(); let len_chars = text.chars().count();
tabstops.push((tabstop, Range::new(offset, offset + len_chars + 1))); tabstops.push((tabstop, offset, offset + len_chars + 1));
offset += len_chars; offset += len_chars;
insert.push_str(text); insert.push_str(text);
} else { } else {
tabstops.push((tabstop, Range::point(offset))); tabstops.push((tabstop, offset, offset));
} }
} }
other => { other => {
@ -157,24 +157,38 @@ pub fn into_transaction<'a>(
}); });
// sort in ascending order (except for 0, which should always be the last one (per lsp doc)) // sort in ascending order (except for 0, which should always be the last one (per lsp doc))
tabstops.sort_unstable_by_key(|(n, _range)| if *n == 0 { usize::MAX } else { *n }); tabstops.sort_unstable_by_key(|(n, _o1, _o2)| if *n == 0 { usize::MAX } else { *n });
// merge tabstops with the same index (we take advantage of the fact that we just sorted them // merge tabstops with the same index (we take advantage of the fact that we just sorted them
// above to simply look backwards) // above to simply look backwards)
let mut ntabstops = Vec::<SmallVec<[Range; 1]>>::new(); let mut ntabstops = Vec::<SmallVec<[(usize, usize); 1]>>::new();
let mut prev = None; {
for (tabstop, range) in tabstops { let mut prev = None;
if prev == Some(tabstop) { for (tabstop, o1, o2) in tabstops {
let len_1 = ntabstops.len() - 1; if prev == Some(tabstop) {
ntabstops[len_1].push(range); let len_1 = ntabstops.len() - 1;
} else { ntabstops[len_1].push((o1, o2));
prev = Some(tabstop); } else {
ntabstops.push(smallvec![range]); prev = Some(tabstop);
ntabstops.push(smallvec![(o1, o2)]);
}
} }
} }
if let Some(first) = ntabstops.first() { if let Some(first) = ntabstops.first() {
transaction.with_selection(Selection::new(first.clone(), 0)) let cursor_offset = insert.chars().count() as i128 - (end_offset - start_offset);
let mut extra_offset = start_offset;
transaction.with_selection(selection.clone().transform_iter(|range| {
let cursor = range.cursor(text);
let iter = first.iter().map(move |first| {
Range::new(
(cursor as i128 + first.0 as i128 + extra_offset) as usize,
(cursor as i128 + first.1 as i128 + extra_offset) as usize,
)
});
extra_offset += cursor_offset;
iter
}))
} else { } else {
transaction transaction
} }

Loading…
Cancel
Save