on master help sign in
lsp: edit events change ranges need to affect each other.
d5f9622Blaž Hrastnik
helix-lsp/src/client.rs+41−6
@@ -247,19 +247,47 @@ impl Client {
247 .await
247 .await
248 }
248 }
249
249
250+ // TODO: this is dumb. TextEdit describes changes to the initial doc (concurrent), but
251+ // TextDocumentContentChangeEvent describes a series of changes (sequential).
252+ // So S -> S1 -> S2, meaning positioning depends on the previous edits.
253+ //
254+ // Calculation is therefore a bunch trickier.
250 pub fn changeset_to_changes(
255 pub fn changeset_to_changes(
251 old_text: &Rope,
256 old_text: &Rope,
257+ new_text: &Rope,
252 changeset: &ChangeSet,
258 changeset: &ChangeSet,
253 ) -> Vec<lsp::TextDocumentContentChangeEvent> {
259 ) -> Vec<lsp::TextDocumentContentChangeEvent> {
254 let mut iter = changeset.changes().iter().peekable();
260 let mut iter = changeset.changes().iter().peekable();
255 let mut old_pos = 0;
261 let mut old_pos = 0;
262+ let mut new_pos = 0;
256
263
257 let mut changes = Vec::new();
264 let mut changes = Vec::new();
258
265
259 use crate::util::pos_to_lsp_pos;
266 use crate::util::pos_to_lsp_pos;
260 use helix_core::Operation::*;
267 use helix_core::Operation::*;
261
268
269+ // TODO: stolen from syntax.rs, share
270+ use helix_core::RopeSlice;
271+ fn traverse(pos: lsp::Position, text: RopeSlice) -> lsp::Position {
272+ let lsp::Position {
273+ mut line,
274+ mut character,
275+ } = pos;
276+
277+ // TODO: there should be a better way here
278+ for ch in text.chars() {
279+ if ch == '\n' {
280+ line += 1;
281+ character = 0;
282+ } else {
283+ character += ch.len_utf16() as u32;
284+ }
285+ }
286+ lsp::Position { line, character }
287+ }
288+
262 let old_text = old_text.slice(..);
289 let old_text = old_text.slice(..);
290+ let new_text = new_text.slice(..);
263
291
264 // TODO: verify this function, specifically line num counting
292 // TODO: verify this function, specifically line num counting
265
293
@@ -271,10 +299,12 @@ impl Client {
271 let mut old_end = old_pos + len;
299 let mut old_end = old_pos + len;
272
300
273 match change {
301 match change {
274− Retain(_) => {}
302+ Retain(i) => {
303+ new_pos += i;
304+ }
275 Delete(_) => {
305 Delete(_) => {
276− let start = pos_to_lsp_pos(&old_text, old_pos);
306+ let start = pos_to_lsp_pos(&new_text, new_pos);
277− let end = pos_to_lsp_pos(&old_text, old_end);
307+ let end = traverse(start, old_text.slice(old_pos..old_end));
278
308
279 // deletion
309 // deletion
280 changes.push(lsp::TextDocumentContentChangeEvent {
310 changes.push(lsp::TextDocumentContentChangeEvent {
@@ -284,12 +314,14 @@ impl Client {
284 });
314 });
285 }
315 }
286 Insert(s) => {
316 Insert(s) => {
287− let start = pos_to_lsp_pos(&old_text, old_pos);
317+ let start = pos_to_lsp_pos(&new_text, new_pos);
318+
319+ new_pos += s.chars().count();
288
320
289 // a subsequent delete means a replace, consume it
321 // a subsequent delete means a replace, consume it
290 let end = if let Some(Delete(len)) = iter.peek() {
322 let end = if let Some(Delete(len)) = iter.peek() {
291 old_end = old_pos + len;
323 old_end = old_pos + len;
292− let end = pos_to_lsp_pos(&old_text, old_end);
324+ let end = traverse(start, old_text.slice(old_pos..old_end));
293
325
294 iter.next();
326 iter.next();
295
327
@@ -318,6 +350,7 @@ impl Client {
318 &self,
350 &self,
319 text_document: lsp::VersionedTextDocumentIdentifier,
351 text_document: lsp::VersionedTextDocumentIdentifier,
320 old_text: &Rope,
352 old_text: &Rope,
353+ new_text: &Rope,
321 changes: &ChangeSet,
354 changes: &ChangeSet,
322 ) -> Result<()> {
355 ) -> Result<()> {
323 // figure out what kind of sync the server supports
356 // figure out what kind of sync the server supports
@@ -343,7 +376,9 @@ impl Client {
343 text: "".to_string(),
376 text: "".to_string(),
344 }] // TODO: probably need old_state here too?
377 }] // TODO: probably need old_state here too?
345 }
378 }
379+ lsp::TextDocumentSyncKind::Incremental => {
346− lsp::TextDocumentSyncKind::Incremental => Self::changeset_to_changes(old_text, changes),
380+ Self::changeset_to_changes(old_text, new_text, changes)
381+ }
347 lsp::TextDocumentSyncKind::None => return Ok(()),
382 lsp::TextDocumentSyncKind::None => return Ok(()),
348 };
383 };
349
384
helix-view/src/document.rs+41−14
@@ -183,6 +183,7 @@ impl Document {
183 let notify = language_server.text_document_did_change(
183 let notify = language_server.text_document_did_change(
184 self.versioned_identifier(),
184 self.versioned_identifier(),
185 &old_doc,
185 &old_doc,
186+ self.text(),
186 transaction.changes(),
187 transaction.changes(),
187 );
188 );
188
189
@@ -301,8 +302,8 @@ mod test {
301
302
302 let transaction = Transaction::insert(&doc.state, " world".into());
303 let transaction = Transaction::insert(&doc.state, " world".into());
303 let old_doc = doc.state.clone();
304 let old_doc = doc.state.clone();
304− let changes = Client::changeset_to_changes(&old_doc.doc, transaction.changes());
305 doc.apply(&transaction);
305 doc.apply(&transaction);
306+ let changes = Client::changeset_to_changes(&old_doc.doc, doc.text(), transaction.changes());
306
307
307 assert_eq!(
308 assert_eq!(
308 changes,
309 changes,
@@ -320,8 +321,8 @@ mod test {
320
321
321 let transaction = transaction.invert(&old_doc);
322 let transaction = transaction.invert(&old_doc);
322 let old_doc = doc.state.clone();
323 let old_doc = doc.state.clone();
323− let changes = Client::changeset_to_changes(&old_doc.doc, transaction.changes());
324 doc.apply(&transaction);
324 doc.apply(&transaction);
325+ let changes = Client::changeset_to_changes(&old_doc.doc, doc.text(), transaction.changes());
325
326
326 // line: 0-based.
327 // line: 0-based.
327 // col: 0-based, gaps between chars.
328 // col: 0-based, gaps between chars.
@@ -343,22 +344,48 @@ mod test {
343
344
344 // replace
345 // replace
345
346
347+ // also tests that changes are layered, positions depend on previous changes.
348+
346 doc.state.selection = Selection::single(0, 5);
349 doc.state.selection = Selection::single(0, 5);
347− let transaction = Transaction::change_by_selection(&doc.state, |range| {
350+ let transaction = Transaction::change(
348− (range.from(), range.to(), Some("aeiou".into()))
351+ &doc.state,
349− });
352+ vec![(0, 2, Some("aei".into())), (3, 5, Some("ou".into()))].into_iter(),
353+ );
354+ // aeilou
355+ doc.apply(&transaction);
356+ let changes =
350− let changes = Client::changeset_to_changes(&doc.state.doc, transaction.changes());
357+ Client::changeset_to_changes(&doc.state.doc, doc.text(), transaction.changes());
351
358
352 assert_eq!(
359 assert_eq!(
353 changes,
360 changes,
361+ &[
362+ // 0 1 2 3 4 5
363+ // |h|e|l|l|o|
364+ // ----
365+ //
366+ // aeillo
354− &[lsp::TextDocumentContentChangeEvent {
367+ lsp::TextDocumentContentChangeEvent {
355− range: Some(lsp::Range::new(
368+ range: Some(lsp::Range::new(
356− lsp::Position::new(0, 0),
369+ lsp::Position::new(0, 0),
370+ lsp::Position::new(0, 2)
371+ )),
372+ text: "aei".into(),
373+ range_length: None,
374+ },
375+ // 0 1 2 3 4 5 6
376+ // |a|e|i|l|l|o|
377+ // -----
378+ //
379+ // aeilou
380+ lsp::TextDocumentContentChangeEvent {
381+ range: Some(lsp::Range::new(
382+ lsp::Position::new(0, 4),
357− lsp::Position::new(0, 5)
383+ lsp::Position::new(0, 6)
358− )),
384+ )),
359− text: "aeiou".into(),
385+ text: "ou".into(),
360− range_length: None,
386+ range_length: None,
361− }]
387+ }
388+ ]
362 );
389 );
363 }
390 }
364}
391}