on master help sign in
robustly handle invalid LSP ranges (#6512)
1073dd6Pascal Kuthe
helix-lsp/src/lib.rs+20−5
@@ -132,7 +132,11 @@ pub mod util {
132 ) -> Option<usize> {
132 ) -> Option<usize> {
133 let pos_line = pos.line as usize;
133 let pos_line = pos.line as usize;
134 if pos_line > doc.len_lines() - 1 {
134 if pos_line > doc.len_lines() - 1 {
135− return None;
135+ // If it extends past the end, truncate it to the end. This is because the
136+ // way the LSP describes the range including the last newline is by
137+ // specifying a line number after what we would call the last line.
138+ log::warn!("LSP position {pos:?} out of range assuming EOF");
139+ return Some(doc.len_chars());
136 }
140 }
137
141
138 // We need to be careful here to fully comply ith the LSP spec.
142 // We need to be careful here to fully comply ith the LSP spec.
@@ -242,9 +246,20 @@ pub mod util {
242
246
243 pub fn lsp_range_to_range(
247 pub fn lsp_range_to_range(
244 doc: &Rope,
248 doc: &Rope,
245− range: lsp::Range,
249+ mut range: lsp::Range,
246 offset_encoding: OffsetEncoding,
250 offset_encoding: OffsetEncoding,
247 ) -> Option<Range> {
251 ) -> Option<Range> {
252+ // This is sort of an edgecase. It's not clear from the spec how to deal with
253+ // ranges where end < start. They don't make much sense but vscode simply caps start to end
254+ // and because it's not specified quite a few LS rely on this as a result (for example the TS server)
255+ if range.start > range.end {
256+ log::error!(
257+ "Invalid LSP range start {:?} > end {:?}, using an empty range at the end instead",
258+ range.start,
259+ range.end
260+ );
261+ range.start = range.end;
262+ }
248 let start = lsp_pos_to_pos(doc, range.start, offset_encoding)?;
263 let start = lsp_pos_to_pos(doc, range.start, offset_encoding)?;
249 let end = lsp_pos_to_pos(doc, range.end, offset_encoding)?;
264 let end = lsp_pos_to_pos(doc, range.end, offset_encoding)?;
250
265
@@ -951,16 +966,16 @@ mod tests {
951
966
952 test_case!("", (0, 0) => Some(0));
967 test_case!("", (0, 0) => Some(0));
953 test_case!("", (0, 1) => Some(0));
968 test_case!("", (0, 1) => Some(0));
954− test_case!("", (1, 0) => None);
969+ test_case!("", (1, 0) => Some(0));
955 test_case!("\n\n", (0, 0) => Some(0));
970 test_case!("\n\n", (0, 0) => Some(0));
956 test_case!("\n\n", (1, 0) => Some(1));
971 test_case!("\n\n", (1, 0) => Some(1));
957 test_case!("\n\n", (1, 1) => Some(1));
972 test_case!("\n\n", (1, 1) => Some(1));
958 test_case!("\n\n", (2, 0) => Some(2));
973 test_case!("\n\n", (2, 0) => Some(2));
959− test_case!("\n\n", (3, 0) => None);
974+ test_case!("\n\n", (3, 0) => Some(2));
960 test_case!("test\n\n\n\ncase", (4, 3) => Some(11));
975 test_case!("test\n\n\n\ncase", (4, 3) => Some(11));
961 test_case!("test\n\n\n\ncase", (4, 4) => Some(12));
976 test_case!("test\n\n\n\ncase", (4, 4) => Some(12));
962 test_case!("test\n\n\n\ncase", (4, 5) => Some(12));
977 test_case!("test\n\n\n\ncase", (4, 5) => Some(12));
963− test_case!("", (u32::MAX, u32::MAX) => None);
978+ test_case!("", (u32::MAX, u32::MAX) => Some(0));
964 }
979 }
965
980
966 #[test]
981 #[test]
helix-term/src/ui/completion.rs+6−10
@@ -141,16 +141,12 @@ impl Completion {
141 }
141 }
142 };
142 };
143
143
144− let start_offset =
144+ let Some(range) = util::lsp_range_to_range(doc.text(), edit.range, offset_encoding) else{
145− match util::lsp_pos_to_pos(doc.text(), edit.range.start, offset_encoding) {
145+ return Transaction::new(doc.text());
146+ };
147+
146− Some(start) => start as i128 - primary_cursor as i128,
148+ let start_offset = range.anchor as i128 - primary_cursor as i128;
147− None => return Transaction::new(doc.text()),
148− };
149− let end_offset =
150− match util::lsp_pos_to_pos(doc.text(), edit.range.end, offset_encoding) {
151− Some(end) => end as i128 - primary_cursor as i128,
149+ let end_offset = range.head as i128 - primary_cursor as i128;
152− None => return Transaction::new(doc.text()),
153− };
154
150
155 (Some((start_offset, end_offset)), edit.new_text)
151 (Some((start_offset, end_offset)), edit.new_text)
156 } else {
152 } else {