From d4a2eeba661ff7a59a908d291f4e81b5b1c090a6 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 2 Oct 2026 16:38:57 +0200 Subject: [PATCH] editor: Don't overwrite primary selection with only empty cursors With several cursors and nothing selected, the per-selection text joined into a string of newlines, which passed the `!text.is_empty()` check and replaced the primary selection on every keystroke and cursor move. Only write to the primary selection when at least one selection is non-empty. Closes #64446 --- crates/editor/src/editor_tests.rs | 31 +++++++++++++++++++++++++++++++ crates/editor/src/selection.rs | 4 +++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 917a70e..7dfc582 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -10996,6 +10996,37 @@ async fn test_copy_and_paste_non_empty_selection_followed_by_empty_selection( "}); } +#[cfg(any(target_os = "linux", target_os = "freebsd"))] +#[gpui::test] +async fn test_empty_cursors_do_not_overwrite_primary_selection(cx: &mut TestAppContext) { + init_test(cx, |_| {}); + + let mut cx = EditorTestContext::new(cx).await; + cx.update(|_, cx| cx.write_to_primary(ClipboardItem::new_string("primary".into()))); + + cx.set_state(indoc! {" + oneˇ + twoˇ + threeˇ + "}); + assert_eq!( + cx.update(|_, cx| cx.read_from_primary()) + .and_then(|item| item.text()), + Some("primary".to_string()) + ); + + cx.set_state(indoc! {" + «oneˇ» + twoˇ + three + "}); + assert_eq!( + cx.update(|_, cx| cx.read_from_primary()) + .and_then(|item| item.text()), + Some("one\n".to_string()) + ); +} + #[gpui::test] async fn test_copy_trim(cx: &mut TestAppContext) { init_test(cx, |_| {}); diff --git a/crates/editor/src/selection.rs b/crates/editor/src/selection.rs index 97ca4b4..66156c8 100644 --- a/crates/editor/src/selection.rs +++ b/crates/editor/src/selection.rs @@ -1562,7 +1562,9 @@ impl Editor { } } - if !text.is_empty() { + // Several empty cursors still join into a string of newlines, so + // check the selections themselves rather than the joined text. + if selections.iter().any(|selection| !selection.is_empty()) { cx.write_to_primary(ClipboardItem::new_string(text)); } }