on master help sign in
lsp: Improve line ending handling when generating TextEdit
57ed518Blaž Hrastnik
helix-lsp/src/client.rs+7−3
@@ -3,7 +3,7 @@ use crate::{
3 Call, Error, OffsetEncoding, Result,
3 Call, Error, OffsetEncoding, Result,
4};
4};
5
5
6−use helix_core::{chars::char_is_line_ending, find_root, ChangeSet, Rope};
6+use helix_core::{find_root, ChangeSet, Rope};
7use jsonrpc_core as jsonrpc;
7use jsonrpc_core as jsonrpc;
8use lsp_types as lsp;
8use lsp_types as lsp;
9use serde_json::Value;
9use serde_json::Value;
@@ -356,7 +356,6 @@ impl Client {
356 //
356 //
357 // Calculation is therefore a bunch trickier.
357 // Calculation is therefore a bunch trickier.
358
358
359− // TODO: stolen from syntax.rs, share
360 use helix_core::RopeSlice;
359 use helix_core::RopeSlice;
361 fn traverse(pos: lsp::Position, text: RopeSlice) -> lsp::Position {
360 fn traverse(pos: lsp::Position, text: RopeSlice) -> lsp::Position {
362 let lsp::Position {
361 let lsp::Position {
@@ -366,7 +365,12 @@ impl Client {
366
365
367 let mut chars = text.chars().peekable();
366 let mut chars = text.chars().peekable();
368 while let Some(ch) = chars.next() {
367 while let Some(ch) = chars.next() {
368+ // LSP only considers \n, \r or \r\n as line endings
369+ if ch == '\n' || ch == '\r' {
370+ // consume a \r\n
369− if char_is_line_ending(ch) && !(ch == '\r' && chars.peek() == Some(&'\n')) {
371+ if chars.peek() == Some(&'\n') {
372+ chars.next();
373+ }
370 line += 1;
374 line += 1;
371 character = 0;
375 character = 0;
372 } else {
376 } else {
helix-view/src/document.rs+34
@@ -891,6 +891,40 @@ impl Default for Document {
891mod test {
891mod test {
892 use super::*;
892 use super::*;
893
893
894+ #[test]
895+ fn changeset_to_changes_ignore_line_endings() {
896+ use helix_lsp::{lsp, Client, OffsetEncoding};
897+ let text = Rope::from("hello\r\nworld");
898+ let mut doc = Document::from(text, None);
899+ let view = ViewId::default();
900+ doc.set_selection(view, Selection::single(0, 0));
901+
902+ let transaction =
903+ Transaction::change(doc.text(), vec![(5, 7, Some("\n".into()))].into_iter());
904+ let old_doc = doc.text().clone();
905+ doc.apply(&transaction, view);
906+ let changes = Client::changeset_to_changes(
907+ &old_doc,
908+ doc.text(),
909+ transaction.changes(),
910+ OffsetEncoding::Utf8,
911+ );
912+
913+ assert_eq!(doc.text(), "hello\nworld");
914+
915+ assert_eq!(
916+ changes,
917+ &[lsp::TextDocumentContentChangeEvent {
918+ range: Some(lsp::Range::new(
919+ lsp::Position::new(0, 5),
920+ lsp::Position::new(1, 0)
921+ )),
922+ text: "\n".into(),
923+ range_length: None,
924+ }]
925+ );
926+ }
927+
894 #[test]
928 #[test]
895 fn changeset_to_changes() {
929 fn changeset_to_changes() {
896 use helix_lsp::{lsp, Client, OffsetEncoding};
930 use helix_lsp::{lsp, Client, OffsetEncoding};