From 46d19d8a47ca059913c2fcb1a06c6543a62b434c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 22 Sep 2025 23:03:37 +0200 Subject: [PATCH] helix: Fix helix-paste mode in line mode (#38663) In particular, * if the selection ends at the beginning of the next line, and the current line under the cursor is empty, we paste at the selection's end. * if however the current line under the cursor is empty, we need to move to the beginning of the next line to avoid pasting above the end of current selection In addition, in line mode, we always move the cursor to the end of the inserted text. Otherwise, while it looks fine visually, inserting/appending ends up in the next logical line which is not desirable. Release Notes: - N/A --- crates/vim/src/helix/paste.rs | 52 +++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/crates/vim/src/helix/paste.rs b/crates/vim/src/helix/paste.rs index ecfdaa499257ad91d8518f488be9a4d4dbb51f1c..957d459dac50892e8173f4f1ac12459277b6d6ae 100644 --- a/crates/vim/src/helix/paste.rs +++ b/crates/vim/src/helix/paste.rs @@ -84,13 +84,22 @@ impl Vim { let display_point = if line_mode { if action.before { movement::line_beginning(&display_map, sel.start, false) - } else if sel.end.column() == 0 { + } else if sel.start.column() > 0 + && sel.end.column() == 0 + && sel.start != sel.end + { sel.end } else { - movement::right( - &display_map, - movement::line_end(&display_map, sel.end, false), - ) + let point = movement::line_end(&display_map, sel.end, false); + if sel.end.column() == 0 && point.column() > 0 { + // If the selection ends at the beginning of the next line, and the current line + // under the cursor is not empty, we paste at the selection's end. + sel.end + } else { + // If however the current line under the cursor is empty, we need to move + // to the beginning of the next line to avoid pasting above the end of current selection. + movement::right(&display_map, point) + } } } else if action.before { sel.start @@ -123,6 +132,12 @@ impl Vim { let offset = anchor.to_offset(&snapshot); if action.before { offset.saturating_sub(len)..offset + } else if line_mode { + // In line mode, we always move the cursor to the end of the inserted text. + // Otherwise, while it looks fine visually, inserting/appending ends up + // in the next logical line which is not desirable. + debug_assert!(len > 0); + offset..(offset + len - 1) } else { offset..(offset + len) } @@ -386,8 +401,8 @@ mod test { indoc! {" The quick brown fox jumps over - «n - ˇ»the lazy dog."}, + «nˇ» + the lazy dog."}, Mode::HelixNormal, ); @@ -405,8 +420,27 @@ mod test { indoc! {" The quick brown fox jumps over - «n - ˇ»the lazy dog."}, + «nˇ» + the lazy dog."}, + Mode::HelixNormal, + ); + + cx.set_state( + indoc! {" + + The quick brown + fox jumps overˇ + the lazy dog."}, + Mode::HelixNormal, + ); + cx.simulate_keystrokes("x y up up p"); + cx.assert_state( + indoc! {" + + «fox jumps overˇ» + The quick brown + fox jumps over + the lazy dog."}, Mode::HelixNormal, ); }