Use the new batched edit API when pasting

Antonio Scandurra created

This fixes a bug that would cause zed to paste text at the wrong
location when inside a multi-buffer where the same buffer was excerpted
more than once.

Change summary

crates/editor/src/editor.rs | 54 ++++++++++++++++++--------------------
1 file changed, 26 insertions(+), 28 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -3450,10 +3450,10 @@ impl Editor {
             if let Some(item) = cx.as_mut().read_from_clipboard() {
                 let mut clipboard_text = Cow::Borrowed(item.text());
                 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
-                    let mut selections = this.local_selections::<usize>(cx);
+                    let old_selections = this.local_selections::<usize>(cx);
                     let all_selections_were_entire_line =
                         clipboard_selections.iter().all(|s| s.is_entire_line);
-                    if clipboard_selections.len() != selections.len() {
+                    if clipboard_selections.len() != old_selections.len() {
                         let mut newline_separated_text = String::new();
                         let mut clipboard_selections = clipboard_selections.drain(..).peekable();
                         let mut ix = 0;
@@ -3468,44 +3468,42 @@ impl Editor {
                         clipboard_text = Cow::Owned(newline_separated_text);
                     }
 
-                    let mut delta = 0_isize;
-                    let mut start_offset = 0;
-                    for (i, selection) in selections.iter_mut().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;
-                        }
-
-                        selection.start = (selection.start as isize + delta) as usize;
-                        selection.end = (selection.end as isize + delta) as usize;
+                    this.buffer.update(cx, |buffer, cx| {
+                        let snapshot = buffer.read(cx);
+                        let mut start_offset = 0;
+                        let mut edits = Vec::new();
+                        for (ix, selection) in old_selections.iter().enumerate() {
+                            let to_insert;
+                            let entire_line;
+                            if let Some(clipboard_selection) = clipboard_selections.get(ix) {
+                                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;
+                            }
 
-                        this.buffer.update(cx, |buffer, cx| {
                             // If the corresponding selection was empty when this slice of the
                             // clipboard text was written, then the entire line containing the
                             // selection was copied. If this selection is also currently empty,
                             // then paste the line before the current line of the buffer.
                             let range = if selection.is_empty() && entire_line {
-                                let column =
-                                    selection.start.to_point(&buffer.read(cx)).column as usize;
+                                let column = selection.start.to_point(&snapshot).column as usize;
                                 let line_start = selection.start - column;
                                 line_start..line_start
                             } else {
                                 selection.start..selection.end
                             };
 
-                            delta += to_insert.len() as isize - range.len() as isize;
-                            buffer.edit([(range, to_insert)], cx);
-                            selection.start += to_insert.len();
-                            selection.end = selection.start;
-                        });
-                    }
+                            edits.push((range, to_insert));
+                        }
+                        drop(snapshot);
+                        buffer.edit_with_autoindent(edits, cx);
+                    });
+
+                    let selections = this.local_selections::<usize>(cx);
                     this.update_selections(selections, Some(Autoscroll::Fit), cx);
                 } else {
                     this.insert(&clipboard_text, cx);