From 0ecefe031ee8a5bd43d6d9a3e94e110cd1febf64 Mon Sep 17 00:00:00 2001 From: Dino Date: Tue, 13 Jan 2026 11:57:06 +0000 Subject: [PATCH] editor: Fix crash when pasting after copy and trim in visual line mode (#46640) When using the `editor::actions::CopyAndTrim` action with a multi-line selection in vim's Visual Line mode, pasting would crash Zed. The bug occurred because trimming splits a selection into per-line ranges, creating multiple `editor::ClipboardSelection` entries. However, when `is_entire_line` was true (Visual Line mode), no newline separators were added between these entries in the clipboard text. The paste code then assumed separators existed and read past the end of the text. The fix ensures newline separators are always added between trimmed line ranges, regardless of whether the original selection was in line mode. Closes #46616 Release Notes: - Fixed a crash when pasting after using `editor: copy and trim` in vim's Visual Line mode --- crates/editor/src/editor.rs | 5 +++-- crates/editor/src/editor_tests.rs | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 1e91a87181..3b78bff391 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -13272,13 +13272,14 @@ impl Editor { trimmed_selections.push(start..end); } + let is_multiline_trim = trimmed_selections.len() > 1; for trimmed_range in trimmed_selections { if is_first { is_first = false; - } else if !prev_selection_was_entire_line { + } else if is_multiline_trim || !prev_selection_was_entire_line { text += "\n"; } - prev_selection_was_entire_line = is_entire_line; + prev_selection_was_entire_line = is_entire_line && !is_multiline_trim; let mut len = 0; for chunk in buffer.text_for_range(trimmed_range.start..trimmed_range.end) { text.push_str(chunk); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 99572e2b5f..fe0169cabf 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -7612,6 +7612,25 @@ if is_entire_line { ); } +#[gpui::test] +async fn test_copy_trim_line_mode(cx: &mut TestAppContext) { + init_test(cx, |_| {}); + + let mut cx = EditorTestContext::new(cx).await; + + cx.set_state(indoc! {" + « a + bˇ» + "}); + cx.update_editor(|editor, _window, _cx| editor.selections.set_line_mode(true)); + cx.update_editor(|editor, window, cx| editor.copy_and_trim(&CopyAndTrim, window, cx)); + + assert_eq!( + cx.read_from_clipboard().and_then(|item| item.text()), + Some("a\nb\n".to_string()) + ); +} + #[gpui::test] async fn test_paste_multiline(cx: &mut TestAppContext) { init_test(cx, |_| {});