diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 057d0b223bb43b41c863316010648005b3675119..0d3325ff8212b6ae9dcc5a9c34dd13e4c5324178 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -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; diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 36d7023db33587e43260640782f47522dbb41c6b..0b485d4e1adac071a82d1ad8bde53f07d14f1434 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -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, |_| {});