Fix bytes/chars error in Editor::paste

Max Brunsfeld created

Fixes #156

Change summary

zed/src/editor.rs | 53 +++++++++++++++++++++++++-----------------------
1 file changed, 28 insertions(+), 25 deletions(-)

Detailed changes

zed/src/editor.rs 🔗

@@ -29,7 +29,6 @@ use smol::Timer;
 use std::{
     cell::RefCell,
     cmp::{self, Ordering},
-    iter::FromIterator,
     mem,
     ops::{Range, RangeInclusive},
     path::Path,
@@ -1219,23 +1218,27 @@ impl Editor {
             let clipboard_text = item.text();
             if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
                 let selections = self.selections(cx.as_ref()).to_vec();
+                let all_selections_were_entire_line =
+                    clipboard_selections.iter().all(|s| s.is_entire_line);
                 if clipboard_selections.len() != selections.len() {
-                    let merged_selection = ClipboardSelection {
-                        len: clipboard_selections.iter().map(|s| s.len).sum(),
-                        is_entire_line: clipboard_selections.iter().all(|s| s.is_entire_line),
-                    };
                     clipboard_selections.clear();
-                    clipboard_selections.push(merged_selection);
                 }
 
                 self.start_transaction(cx);
+                let mut start_offset = 0;
                 let mut new_selections = Vec::with_capacity(selections.len());
-                let mut clipboard_chars = clipboard_text.chars().cycle();
-                for (selection, clipboard_selection) in
-                    selections.iter().zip(clipboard_selections.iter().cycle())
-                {
-                    let to_insert =
-                        String::from_iter(clipboard_chars.by_ref().take(clipboard_selection.len));
+                for (i, selection) in selections.iter().enumerate() {
+                    let to_insert;
+                    let entire_line;
+                    if let Some(clipboard_selection) = clipboard_selections.get(i) {
+                        let end_offset = start_offset + clipboard_selection.len;
+                        to_insert = &clipboard_text[start_offset..end_offset];
+                        entire_line = clipboard_selection.is_entire_line;
+                        start_offset = end_offset
+                    } else {
+                        to_insert = clipboard_text.as_str();
+                        entire_line = all_selections_were_entire_line;
+                    }
 
                     self.buffer.update(cx, |buffer, cx| {
                         let selection_start = selection.start.to_point(&*buffer);
@@ -1246,7 +1249,7 @@ impl Editor {
                         // selection was copied. If this selection is also currently empty,
                         // then paste the line before the current line of the buffer.
                         let new_selection_start = selection.end.bias_right(buffer);
-                        if selection_start == selection_end && clipboard_selection.is_entire_line {
+                        if selection_start == selection_end && entire_line {
                             let line_start = Point::new(selection_start.row, 0);
                             buffer.edit(Some(line_start..line_start), to_insert, cx);
                         } else {
@@ -3685,7 +3688,7 @@ mod tests {
 
     #[gpui::test]
     fn test_clipboard(cx: &mut gpui::MutableAppContext) {
-        let buffer = cx.add_model(|cx| Buffer::new(0, "one two three four five six ", cx));
+        let buffer = cx.add_model(|cx| Buffer::new(0, "one✅ two three four five six ", cx));
         let settings = settings::test(&cx).1;
         let view = cx
             .add_window(Default::default(), |cx| {
@@ -3695,7 +3698,7 @@ mod tests {
 
         // Cut with three selections. Clipboard text is divided into three slices.
         view.update(cx, |view, cx| {
-            view.select_ranges(vec![0..4, 8..14, 19..24], false, cx);
+            view.select_ranges(vec![0..7, 11..17, 22..27], false, cx);
             view.cut(&Cut, cx);
             assert_eq!(view.display_text(cx), "two four six ");
         });
@@ -3704,13 +3707,13 @@ mod tests {
         view.update(cx, |view, cx| {
             view.select_ranges(vec![4..4, 9..9, 13..13], false, cx);
             view.paste(&Paste, cx);
-            assert_eq!(view.display_text(cx), "two one four three six five ");
+            assert_eq!(view.display_text(cx), "two one✅ four three six five ");
             assert_eq!(
                 view.selection_ranges(cx),
                 &[
-                    DisplayPoint::new(0, 8)..DisplayPoint::new(0, 8),
-                    DisplayPoint::new(0, 19)..DisplayPoint::new(0, 19),
-                    DisplayPoint::new(0, 28)..DisplayPoint::new(0, 28)
+                    DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
+                    DisplayPoint::new(0, 22)..DisplayPoint::new(0, 22),
+                    DisplayPoint::new(0, 31)..DisplayPoint::new(0, 31)
                 ]
             );
         });
@@ -3719,13 +3722,13 @@ mod tests {
         // match the number of slices in the clipboard, the entire clipboard text
         // is pasted at each cursor.
         view.update(cx, |view, cx| {
-            view.select_ranges(vec![0..0, 28..28], false, cx);
+            view.select_ranges(vec![0..0, 31..31], false, cx);
             view.insert(&Insert("( ".into()), cx);
             view.paste(&Paste, cx);
             view.insert(&Insert(") ".into()), cx);
             assert_eq!(
                 view.display_text(cx),
-                "( one three five ) two one four three six five ( one three five ) "
+                "( one✅ three five ) two one✅ four three six five ( one✅ three five ) "
             );
         });
 
@@ -3734,7 +3737,7 @@ mod tests {
             view.insert(&Insert("123\n4567\n89\n".into()), cx);
             assert_eq!(
                 view.display_text(cx),
-                "123\n4567\n89\n( one three five ) two one four three six five ( one three five ) "
+                "123\n4567\n89\n( one✅ three five ) two one✅ four three six five ( one✅ three five ) "
             );
         });
 
@@ -3752,7 +3755,7 @@ mod tests {
             view.cut(&Cut, cx);
             assert_eq!(
                 view.display_text(cx),
-                "13\n9\n( one three five ) two one four three six five ( one three five ) "
+                "13\n9\n( one✅ three five ) two one✅ four three six five ( one✅ three five ) "
             );
         });
 
@@ -3771,7 +3774,7 @@ mod tests {
             view.paste(&Paste, cx);
             assert_eq!(
                 view.display_text(cx),
-                "123\n4567\n9\n( 8ne three five ) two one four three six five ( one three five ) "
+                "123\n4567\n9\n( 8ne✅ three five ) two one✅ four three six five ( one✅ three five ) "
             );
             assert_eq!(
                 view.selection_ranges(cx),
@@ -3805,7 +3808,7 @@ mod tests {
             view.paste(&Paste, cx);
             assert_eq!(
                 view.display_text(cx),
-                "123\n123\n123\n67\n123\n9\n( 8ne three five ) two one four three six five ( one three five ) "
+                "123\n123\n123\n67\n123\n9\n( 8ne✅ three five ) two one✅ four three six five ( one✅ three five ) "
             );
             assert_eq!(
                 view.selection_ranges(cx),