on master help sign in
Implement LSP snippet tabstops sorting and merging
ded4381Urgau
helix-lsp/src/snippet.rs+25−12
@@ -1,6 +1,7 @@
1use std::borrow::Cow;
1use std::borrow::Cow;
2
2
3use anyhow::{anyhow, Result};
3use anyhow::{anyhow, Result};
4+use helix_core::SmallVec;
4
5
5use crate::{util::lsp_pos_to_pos, OffsetEncoding};
6use crate::{util::lsp_pos_to_pos, OffsetEncoding};
6
7
@@ -87,7 +88,7 @@ pub fn into_transaction<'a>(
87
88
88 let mut insert = String::new();
89 let mut insert = String::new();
89 let mut offset = (primary_cursor as i128 + start_offset) as usize;
90 let mut offset = (primary_cursor as i128 + start_offset) as usize;
90− let mut tabstops: Vec<Range> = Vec::new();
91+ let mut tabstops: Vec<(usize, Range)> = Vec::new();
91
92
92 for element in snippet.elements {
93 for element in snippet.elements {
93 match element {
94 match element {
@@ -111,20 +112,15 @@ pub fn into_transaction<'a>(
111 offset += text.chars().count();
112 offset += text.chars().count();
112 insert.push_str(text);
113 insert.push_str(text);
113 }
114 }
114− Tabstop { .. } => {
115+ Tabstop { tabstop } => {
115− // TODO: tabstop indexing: 0 is final cursor position. 1,2,.. are positions.
116− // TODO: merge tabstops with the same index
117− tabstops.push(Range::point(offset));
116+ tabstops.push((tabstop, Range::point(offset)));
118 }
117 }
119− Placeholder {
120− tabstop: _tabstop,
121− value,
122− } => match value.as_ref() {
118+ Placeholder { tabstop, value } => match value.as_ref() {
123 // https://doc.rust-lang.org/beta/unstable-book/language-features/box-patterns.html
119 // https://doc.rust-lang.org/beta/unstable-book/language-features/box-patterns.html
124 // would make this a bit nicer
120 // would make this a bit nicer
125 Text(text) => {
121 Text(text) => {
126 let len_chars = text.chars().count();
122 let len_chars = text.chars().count();
127− tabstops.push(Range::new(offset, offset + len_chars + 1));
123+ tabstops.push((tabstop, Range::new(offset, offset + len_chars + 1)));
128 offset += len_chars;
124 offset += len_chars;
129 insert.push_str(text);
125 insert.push_str(text);
130 }
126 }
@@ -155,8 +151,25 @@ pub fn into_transaction<'a>(
155 )
151 )
156 });
152 });
157
153
154+ // sort in ascending order (except for 0, which should always be the last one (per lsp doc))
155+ tabstops.sort_unstable_by_key(|(n, _range)| if *n == 0 { usize::MAX } else { *n });
156+
157+ // merge tabstops with the same index (we take advantage of the fact that we just sorted them
158+ // above to simply look backwards)
159+ let mut ntabstops = Vec::<SmallVec<[Range; 1]>>::new();
160+ let mut prev = None;
161+ for (tabstop, range) in tabstops {
162+ if prev == Some(tabstop) {
163+ let len_1 = ntabstops.len() - 1;
164+ ntabstops[len_1].push(range);
165+ } else {
166+ prev = Some(tabstop);
167+ ntabstops.push(smallvec![range]);
168+ }
169+ }
170+
158− if let Some(first) = tabstops.first() {
171+ if let Some(first) = ntabstops.first() {
159− transaction.with_selection(Selection::new(smallvec![*first], 0))
172+ transaction.with_selection(Selection::new(first.clone(), 0))
160 } else {
173 } else {
161 transaction
174 transaction
162 }
175 }