ep_cli: Further improve reversal ratio (#48028)

Ben Kunkle created

Closes #ISSUE

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Change summary

crates/edit_prediction_cli/src/reversal_tracking.rs | 56 ++++++++++----
1 file changed, 40 insertions(+), 16 deletions(-)

Detailed changes

crates/edit_prediction_cli/src/reversal_tracking.rs 🔗

@@ -57,6 +57,7 @@ struct HistoryAdditionRange {
 #[derive(Debug, Clone)]
 struct HistoryDeletionRange {
     deleted_text: String,
+    position_in_current: usize,
 }
 
 fn compute_history_addition_ranges(history_edits: &[GranularEdit]) -> Vec<HistoryAdditionRange> {
@@ -79,13 +80,22 @@ fn compute_history_addition_ranges(history_edits: &[GranularEdit]) -> Vec<Histor
 }
 
 fn compute_history_deletion_ranges(history_edits: &[GranularEdit]) -> Vec<HistoryDeletionRange> {
-    history_edits
-        .iter()
-        .filter(|edit| !edit.old_text.is_empty())
-        .map(|edit| HistoryDeletionRange {
-            deleted_text: edit.old_text.clone(),
-        })
-        .collect()
+    let mut result = Vec::new();
+    let mut offset_delta: isize = 0;
+
+    for edit in history_edits {
+        if !edit.old_text.is_empty() {
+            let position_in_current = (edit.range.start as isize + offset_delta) as usize;
+            result.push(HistoryDeletionRange {
+                deleted_text: edit.old_text.clone(),
+                position_in_current,
+            });
+        }
+
+        offset_delta += edit.new_text.len() as isize - edit.old_text.len() as isize;
+    }
+
+    result
 }
 
 #[derive(Debug, Clone, Default, PartialEq, Eq)]
@@ -214,17 +224,23 @@ fn compute_restored_deletions(
     history_deletion_ranges: &[HistoryDeletionRange],
     prediction_edits: &[GranularEdit],
 ) -> usize {
-    let history_deleted_text: String = history_deletion_ranges
-        .iter()
-        .map(|r| r.deleted_text.as_str())
-        .collect();
+    let mut restored = 0;
 
-    let prediction_added_text: String = prediction_edits
-        .iter()
-        .map(|e| e.new_text.as_str())
-        .collect();
+    for pred_edit in prediction_edits {
+        if pred_edit.new_text.is_empty() {
+            continue;
+        }
 
-    compute_lcs_length(&history_deleted_text, &prediction_added_text)
+        for deletion in history_deletion_ranges {
+            if pred_edit.range.contains(&deletion.position_in_current)
+                || deletion.position_in_current == pred_edit.range.start
+            {
+                restored += compute_lcs_length(&deletion.deleted_text, &pred_edit.new_text);
+            }
+        }
+    }
+
+    restored
 }
 
 fn compute_lcs_length(a: &str, b: &str) -> usize {
@@ -524,6 +540,14 @@ mod tests {
                 expected_reversal_chars: 1,
                 expected_total_chars: 1,
             },
+            Case {
+                name: "multiple insertions no reversal",
+                original: "print(\"Hello, World!\")",
+                current: "sys.(\"Hello, World!\")",
+                predicted: "sys.stdout.write(\"Hello, World!\n\")",
+                expected_reversal_chars: 0,
+                expected_total_chars: 13,
+            },
         ];
 
         for case in &cases {