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
This commit is contained in:
Dino 2026-01-13 11:57:06 +00:00 committed by GitHub
parent 70da176353
commit 0ecefe031e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 22 additions and 2 deletions

View file

@ -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);

View file

@ -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
»
"});
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, |_| {});