@@ -33,16 +33,14 @@ impl Vim {
let selected_register = vim.selected_register.take();
- let Some((text, clipboard_selections)) = Vim::update_globals(cx, |globals, cx| {
+ let Some(register) = Vim::update_globals(cx, |globals, cx| {
globals.read_register(selected_register, Some(editor), cx)
})
- .and_then(|reg| {
- (!reg.text.is_empty())
- .then_some(reg.text)
- .zip(reg.clipboard_selections)
- }) else {
+ .filter(|reg| !reg.text.is_empty()) else {
return;
};
+ let text = register.text;
+ let clipboard_selections = register.clipboard_selections;
let display_map = editor.display_snapshot(cx);
let current_selections = editor.selections.all_adjusted_display(&display_map);
@@ -63,7 +61,9 @@ impl Vim {
let mut replacement_texts: Vec<String> = Vec::new();
for ix in 0..current_selections.len() {
- let to_insert = if let Some(clip_sel) = clipboard_selections.get(ix) {
+ let to_insert = if let Some(clip_sel) =
+ clipboard_selections.as_ref().and_then(|s| s.get(ix))
+ {
let end_offset = start_offset + clip_sel.len;
let text = text[start_offset..end_offset].to_string();
start_offset = if clip_sel.is_entire_line {
@@ -102,13 +102,16 @@ impl Vim {
} else if action.before {
sel.start
} else if sel.start == sel.end {
- // Helix and Zed differ in how they understand
- // single-point cursors. In Helix, a single-point cursor
- // is "on top" of some character, and pasting after that
- // cursor means that the pasted content should go after
- // that character. (If the cursor is at the end of a
- // line, the pasted content goes on the next line.)
- movement::right(&display_map, sel.end)
+ // In Helix, a single-point cursor is "on top" of a
+ // character, and pasting after means after that character.
+ // At line end this means the next line. But on an empty
+ // line there is no character, so paste at the cursor.
+ let right = movement::right(&display_map, sel.end);
+ if right.row() != sel.end.row() && sel.end.column() == 0 {
+ sel.end
+ } else {
+ right
+ }
} else {
sel.end
};
@@ -146,8 +149,58 @@ impl Vim {
mod test {
use indoc::indoc;
+ use gpui::ClipboardItem;
+
use crate::{state::Mode, test::VimTestContext};
+ #[gpui::test]
+ async fn test_system_clipboard_paste(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+ cx.enable_helix();
+ cx.set_state(
+ indoc! {"
+ The quiˇck brown
+ fox jumps over
+ the lazy dog."},
+ Mode::HelixNormal,
+ );
+
+ cx.write_to_clipboard(ClipboardItem::new_string("clipboard".to_string()));
+ cx.simulate_keystrokes("p");
+ cx.assert_state(
+ indoc! {"
+ The quic«clipboardˇ»k brown
+ fox jumps over
+ the lazy dog."},
+ Mode::HelixNormal,
+ );
+
+ // Multiple cursors with system clipboard (no metadata) pastes
+ // the same text at each cursor.
+ cx.set_state(
+ indoc! {"
+ ˇThe quick brown
+ fox ˇjumps over
+ the lazy dog."},
+ Mode::HelixNormal,
+ );
+ cx.write_to_clipboard(ClipboardItem::new_string("hi".to_string()));
+ cx.simulate_keystrokes("p");
+ cx.assert_state(
+ indoc! {"
+ T«hiˇ»he quick brown
+ fox j«hiˇ»umps over
+ the lazy dog."},
+ Mode::HelixNormal,
+ );
+
+ // Multiple cursors on empty lines should paste on those same lines.
+ cx.set_state("ˇ\nˇ\nˇ\nend", Mode::HelixNormal);
+ cx.write_to_clipboard(ClipboardItem::new_string("X".to_string()));
+ cx.simulate_keystrokes("p");
+ cx.assert_state("«Xˇ»\n«Xˇ»\n«Xˇ»\nend", Mode::HelixNormal);
+ }
+
#[gpui::test]
async fn test_paste(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;