Revert "edit prediction: Fix crash in `highlight_text` (#23766)" (#23771)

Bennet Bo Fenner created

This reverts commit dfed43ab24ab9cdf41a5645382d46fa67edfcdd9.

Closes #ISSUE

Release Notes:

- N/A

Change summary

crates/zeta/src/zeta.rs | 103 +++++++++++++++++++++++++-----------------
1 file changed, 61 insertions(+), 42 deletions(-)

Detailed changes

crates/zeta/src/zeta.rs 🔗

@@ -103,23 +103,23 @@ fn interpolate(
 ) -> Option<Vec<(Range<Anchor>, String)>> {
     let mut edits = Vec::new();
 
-    let mut model_edits = current_edits.into_iter().peekable();
-    for user_edit in new_snapshot.edits_since::<usize>(&old_snapshot.version) {
-        while let Some((model_old_range, _)) = model_edits.peek() {
-            let model_old_range = model_old_range.to_offset(old_snapshot);
-            if !model_old_range.is_empty() {
-                return None;
-            } else if model_old_range.end < user_edit.old.start {
-                let (model_old_range, model_new_text) = model_edits.next().unwrap();
-                edits.push((model_old_range.clone(), model_new_text.clone()));
+    let mut user_edits = new_snapshot
+        .edits_since::<usize>(&old_snapshot.version)
+        .peekable();
+    for (model_old_range, model_new_text) in current_edits.iter() {
+        let model_offset_range = model_old_range.to_offset(old_snapshot);
+        while let Some(next_user_edit) = user_edits.peek() {
+            if next_user_edit.old.end < model_offset_range.start {
+                user_edits.next();
             } else {
                 break;
             }
         }
 
-        if let Some((model_old_range, model_new_text)) = model_edits.peek() {
-            let model_old_offset_range = model_old_range.to_offset(old_snapshot);
-            if user_edit.old == model_old_offset_range {
+        if let Some(user_edit) = user_edits.peek() {
+            if user_edit.old.start > model_offset_range.end {
+                edits.push((model_old_range.clone(), model_new_text.clone()));
+            } else if user_edit.old == model_offset_range {
                 let user_new_text = new_snapshot
                     .text_for_range(user_edit.new.clone())
                     .collect::<String>();
@@ -128,26 +128,20 @@ fn interpolate(
                     if !model_suffix.is_empty() {
                         edits.push((
                             new_snapshot.anchor_after(user_edit.new.end)
-                                ..new_snapshot.anchor_after(user_edit.new.end),
-                            model_suffix.to_string(),
+                                ..new_snapshot.anchor_before(user_edit.new.end),
+                            model_suffix.into(),
                         ));
                     }
 
-                    model_edits.next();
-                    continue;
+                    user_edits.next();
+                } else {
+                    return None;
                 }
+            } else {
+                return None;
             }
-        }
-
-        return None;
-    }
-
-    for (model_old_range, model_new_text) in model_edits {
-        let model_old_offset_range = model_old_range.to_offset(old_snapshot);
-        if model_old_offset_range.is_empty() {
-            edits.push((model_old_range.clone(), model_new_text.clone()));
         } else {
-            return None;
+            edits.push((model_old_range.clone(), model_new_text.clone()));
         }
     }
 
@@ -1283,10 +1277,10 @@ mod tests {
 
     #[gpui::test]
     async fn test_inline_completion_basic_interpolation(cx: &mut TestAppContext) {
-        let buffer = cx.new(|cx| Buffer::local("Lo ips dolor", cx));
+        let buffer = cx.new(|cx| Buffer::local("Lorem ipsum dolor", cx));
         let edits: Arc<[(Range<Anchor>, String)]> = cx.update(|cx| {
             to_completion_edits(
-                [(2..2, "REM".to_string()), (6..6, "UM".to_string())],
+                [(2..5, "REM".to_string()), (9..11, "".to_string())],
                 &buffer,
                 cx,
             )
@@ -1320,55 +1314,80 @@ mod tests {
                     &buffer,
                     cx
                 ),
-                vec![(2..2, "REM".to_string()), (6..6, "UM".to_string())]
+                vec![(2..5, "REM".to_string()), (9..11, "".to_string())]
             );
 
-            buffer.update(cx, |buffer, cx| buffer.edit([(2..2, "R")], None, cx));
+            buffer.update(cx, |buffer, cx| buffer.edit([(2..5, "")], None, cx));
             assert_eq!(
                 from_completion_edits(
                     &completion.interpolate(&buffer.read(cx).snapshot()).unwrap(),
                     &buffer,
                     cx
                 ),
-                vec![(3..3, "EM".to_string()), (7..7, "UM".to_string())]
+                vec![(2..2, "REM".to_string()), (6..8, "".to_string())]
             );
 
-            buffer.update(cx, |buffer, cx| buffer.edit([(7..7, "U")], None, cx));
+            buffer.update(cx, |buffer, cx| buffer.undo(cx));
             assert_eq!(
                 from_completion_edits(
                     &completion.interpolate(&buffer.read(cx).snapshot()).unwrap(),
                     &buffer,
                     cx
                 ),
-                vec![(3..3, "EM".to_string()), (8..8, "M".to_string())]
+                vec![(2..5, "REM".to_string()), (9..11, "".to_string())]
             );
 
-            // Editing after all the model edits should always keep the inline completion
-            buffer.update(cx, |buffer, cx| buffer.edit([(10..10, "X")], None, cx));
-            assert_eq!(completion.interpolate(&buffer.read(cx).snapshot()), None);
+            buffer.update(cx, |buffer, cx| buffer.edit([(2..5, "R")], None, cx));
+            assert_eq!(
+                from_completion_edits(
+                    &completion.interpolate(&buffer.read(cx).snapshot()).unwrap(),
+                    &buffer,
+                    cx
+                ),
+                vec![(3..3, "EM".to_string()), (7..9, "".to_string())]
+            );
 
-            buffer.update(cx, |buffer, cx| buffer.undo(cx));
+            buffer.update(cx, |buffer, cx| buffer.edit([(3..3, "E")], None, cx));
+            assert_eq!(
+                from_completion_edits(
+                    &completion.interpolate(&buffer.read(cx).snapshot()).unwrap(),
+                    &buffer,
+                    cx
+                ),
+                vec![(4..4, "M".to_string()), (8..10, "".to_string())]
+            );
+
+            buffer.update(cx, |buffer, cx| buffer.edit([(4..4, "M")], None, cx));
+            assert_eq!(
+                from_completion_edits(
+                    &completion.interpolate(&buffer.read(cx).snapshot()).unwrap(),
+                    &buffer,
+                    cx
+                ),
+                vec![(9..11, "".to_string())]
+            );
+
+            buffer.update(cx, |buffer, cx| buffer.edit([(4..5, "")], None, cx));
             assert_eq!(
                 from_completion_edits(
                     &completion.interpolate(&buffer.read(cx).snapshot()).unwrap(),
                     &buffer,
                     cx
                 ),
-                vec![(3..3, "EM".to_string()), (8..8, "M".to_string())]
+                vec![(4..4, "M".to_string()), (8..10, "".to_string())]
             );
 
-            buffer.update(cx, |buffer, cx| buffer.edit([(8..8, "M")], None, cx));
+            buffer.update(cx, |buffer, cx| buffer.edit([(8..10, "")], None, cx));
             assert_eq!(
                 from_completion_edits(
                     &completion.interpolate(&buffer.read(cx).snapshot()).unwrap(),
                     &buffer,
                     cx
                 ),
-                vec![(3..3, "EM".to_string())]
+                vec![(4..4, "M".to_string())]
             );
 
-            // Editing before the model edits should always remove the inline completion
-            buffer.update(cx, |buffer, cx| buffer.edit([(0..0, "I")], None, cx));
+            buffer.update(cx, |buffer, cx| buffer.edit([(4..6, "")], None, cx));
             assert_eq!(completion.interpolate(&buffer.read(cx).snapshot()), None);
         })
     }