editor: Use a single newline between each copied line from a multi-cursor selection (#41204)

Closes #40923

Release Notes:

- Fixed the amount of newlines between copied lines from a multi-cursor
selection of multiple full-line copies.

---


https://github.com/user-attachments/assets/ab7474d6-0e49-4c29-9700-7692cd019cef
This commit is contained in:
Sean Hagstrom
2025-11-12 22:58:13 -07:00
committed by GitHub
parent b32559f07d
commit 1936f16c62
2 changed files with 65 additions and 3 deletions
+11 -3
View File
@@ -12585,6 +12585,7 @@ impl Editor {
{
let max_point = buffer.max_point();
let mut is_first = true;
let mut prev_selection_was_entire_line = false;
for selection in &mut selections {
let is_entire_line =
(selection.is_empty() && cut_no_selection_line) || self.selections.line_mode();
@@ -12599,9 +12600,10 @@ impl Editor {
}
if is_first {
is_first = false;
} else {
} else if !prev_selection_was_entire_line {
text += "\n";
}
prev_selection_was_entire_line = is_entire_line;
let mut len = 0;
for chunk in buffer.text_for_range(selection.start..selection.end) {
text.push_str(chunk);
@@ -12684,6 +12686,7 @@ impl Editor {
{
let max_point = buffer.max_point();
let mut is_first = true;
let mut prev_selection_was_entire_line = false;
for selection in &selections {
let mut start = selection.start;
let mut end = selection.end;
@@ -12742,9 +12745,10 @@ impl Editor {
for trimmed_range in trimmed_selections {
if is_first {
is_first = false;
} else {
} else if !prev_selection_was_entire_line {
text += "\n";
}
prev_selection_was_entire_line = is_entire_line;
let mut len = 0;
for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) {
text.push_str(chunk);
@@ -12818,7 +12822,11 @@ impl Editor {
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 + 1;
start_offset = if entire_line {
end_offset
} else {
end_offset + 1
};
original_indent_column = Some(clipboard_selection.first_line_indent);
} else {
to_insert = &*clipboard_text;
+54
View File
@@ -27375,6 +27375,60 @@ async fn test_copy_line_without_trailing_newline(cx: &mut TestAppContext) {
cx.assert_editor_state("line1\nline2\nˇ");
}
#[gpui::test]
async fn test_multi_selection_copy_with_newline_between_copied_lines(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorTestContext::new(cx).await;
cx.set_state("ˇline1\nˇline2\nˇline3\n");
cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx));
let clipboard_text = cx
.read_from_clipboard()
.and_then(|item| item.text().as_deref().map(str::to_string));
assert_eq!(
clipboard_text,
Some("line1\nline2\nline3\n".to_string()),
"Copying multiple lines should include a single newline between lines"
);
cx.set_state("lineA\nˇ");
cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
cx.assert_editor_state("lineA\nline1\nline2\nline3\nˇ");
}
#[gpui::test]
async fn test_multi_selection_cut_with_newline_between_copied_lines(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let mut cx = EditorTestContext::new(cx).await;
cx.set_state("ˇline1\nˇline2\nˇline3\n");
cx.update_editor(|e, window, cx| e.cut(&Cut, window, cx));
let clipboard_text = cx
.read_from_clipboard()
.and_then(|item| item.text().as_deref().map(str::to_string));
assert_eq!(
clipboard_text,
Some("line1\nline2\nline3\n".to_string()),
"Copying multiple lines should include a single newline between lines"
);
cx.set_state("lineA\nˇ");
cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx));
cx.assert_editor_state("lineA\nline1\nline2\nline3\nˇ");
}
#[gpui::test]
async fn test_end_of_editor_context(cx: &mut TestAppContext) {
init_test(cx, |_| {});