on master help sign in
Correctly handle multiple cursors with LSP snippets
ec6e575Urgau
helix-core/src/selection.rs+10
@@ -578,6 +578,16 @@ impl Selection {
578 self.normalize()
578 self.normalize()
579 }
579 }
580
580
581+ /// Takes a closure and maps each `Range` over the closure to multiple `Range`s.
582+ pub fn transform_iter<F, I>(mut self, f: F) -> Self
583+ where
584+ F: FnMut(Range) -> I,
585+ I: Iterator<Item = Range>,
586+ {
587+ self.ranges = self.ranges.into_iter().flat_map(f).collect();
588+ self.normalize()
589+ }
590+
581 // Ensures the selection adheres to the following invariants:
591 // Ensures the selection adheres to the following invariants:
582 // 1. All ranges are grapheme aligned.
592 // 1. All ranges are grapheme aligned.
583 // 2. All ranges are at least 1 character wide, unless at the
593 // 2. All ranges are at least 1 character wide, unless at the
helix-lsp/src/snippet.rs+31−17
@@ -66,7 +66,7 @@ pub fn into_transaction<'a>(
66 offset_encoding: OffsetEncoding,
66 offset_encoding: OffsetEncoding,
67 include_placeholer: bool,
67 include_placeholer: bool,
68) -> helix_core::Transaction {
68) -> helix_core::Transaction {
69− use helix_core::{smallvec, Range, Selection, Transaction};
69+ use helix_core::{smallvec, Range, Transaction};
70 use SnippetElement::*;
70 use SnippetElement::*;
71
71
72 let text = doc.slice(..);
72 let text = doc.slice(..);
@@ -87,9 +87,9 @@ pub fn into_transaction<'a>(
87 blank = ""
87 blank = ""
88 );
88 );
89
89
90+ let mut offset = 0;
90 let mut insert = String::new();
91 let mut insert = String::new();
91− let mut offset = (primary_cursor as i128 + start_offset) as usize;
92− let mut tabstops: Vec<(usize, Range)> = Vec::new();
92+ let mut tabstops: Vec<(usize, usize, usize)> = Vec::new();
93
93
94 for element in snippet.elements {
94 for element in snippet.elements {
95 match element {
95 match element {
@@ -114,7 +114,7 @@ pub fn into_transaction<'a>(
114 insert.push_str(text);
114 insert.push_str(text);
115 }
115 }
116 Tabstop { tabstop } => {
116 Tabstop { tabstop } => {
117− tabstops.push((tabstop, Range::point(offset)));
117+ tabstops.push((tabstop, offset, offset));
118 }
118 }
119 Placeholder { tabstop, value } => match value.as_ref() {
119 Placeholder { tabstop, value } => match value.as_ref() {
120 // https://doc.rust-lang.org/beta/unstable-book/language-features/box-patterns.html
120 // https://doc.rust-lang.org/beta/unstable-book/language-features/box-patterns.html
@@ -122,11 +122,11 @@ pub fn into_transaction<'a>(
122 Text(text) => {
122 Text(text) => {
123 if include_placeholer {
123 if include_placeholer {
124 let len_chars = text.chars().count();
124 let len_chars = text.chars().count();
125− tabstops.push((tabstop, Range::new(offset, offset + len_chars + 1)));
125+ tabstops.push((tabstop, offset, offset + len_chars + 1));
126 offset += len_chars;
126 offset += len_chars;
127 insert.push_str(text);
127 insert.push_str(text);
128 } else {
128 } else {
129− tabstops.push((tabstop, Range::point(offset)));
129+ tabstops.push((tabstop, offset, offset));
130 }
130 }
131 }
131 }
132 other => {
132 other => {
@@ -157,24 +157,38 @@ pub fn into_transaction<'a>(
157 });
157 });
158
158
159 // sort in ascending order (except for 0, which should always be the last one (per lsp doc))
159 // sort in ascending order (except for 0, which should always be the last one (per lsp doc))
160− tabstops.sort_unstable_by_key(|(n, _range)| if *n == 0 { usize::MAX } else { *n });
160+ tabstops.sort_unstable_by_key(|(n, _o1, _o2)| if *n == 0 { usize::MAX } else { *n });
161
161
162 // merge tabstops with the same index (we take advantage of the fact that we just sorted them
162 // merge tabstops with the same index (we take advantage of the fact that we just sorted them
163 // above to simply look backwards)
163 // above to simply look backwards)
164− let mut ntabstops = Vec::<SmallVec<[Range; 1]>>::new();
164+ let mut ntabstops = Vec::<SmallVec<[(usize, usize); 1]>>::new();
165+ {
165− let mut prev = None;
166+ let mut prev = None;
166− for (tabstop, range) in tabstops {
167+ for (tabstop, o1, o2) in tabstops {
167− if prev == Some(tabstop) {
168+ if prev == Some(tabstop) {
168− let len_1 = ntabstops.len() - 1;
169+ let len_1 = ntabstops.len() - 1;
169− ntabstops[len_1].push(range);
170+ ntabstops[len_1].push((o1, o2));
170− } else {
171+ } else {
171− prev = Some(tabstop);
172+ prev = Some(tabstop);
172− ntabstops.push(smallvec![range]);
173+ ntabstops.push(smallvec![(o1, o2)]);
174+ }
173 }
175 }
174 }
176 }
175
177
176 if let Some(first) = ntabstops.first() {
178 if let Some(first) = ntabstops.first() {
179+ let cursor_offset = insert.chars().count() as i128 - (end_offset - start_offset);
180+ let mut extra_offset = start_offset;
177− transaction.with_selection(Selection::new(first.clone(), 0))
181+ transaction.with_selection(selection.clone().transform_iter(|range| {
182+ let cursor = range.cursor(text);
183+ let iter = first.iter().map(move |first| {
184+ Range::new(
185+ (cursor as i128 + first.0 as i128 + extra_offset) as usize,
186+ (cursor as i128 + first.1 as i128 + extra_offset) as usize,
187+ )
188+ });
189+ extra_offset += cursor_offset;
190+ iter
191+ }))
178 } else {
192 } else {
179 transaction
193 transaction
180 }
194 }