@@ -15,6 +15,8 @@ actions!(
1515 [
1616 /// Switches to normal mode after the cursor (Helix-style).
1717 HelixNormalAfter ,
18+ /// Yanks the current selection or character if no selection.
19+ HelixYank ,
1820 /// Inserts at the beginning of the selection.
1921 HelixInsert ,
2022 /// Appends at the end of the selection.
@@ -26,6 +28,7 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
2628 Vim :: action ( editor, cx, Vim :: helix_normal_after) ;
2729 Vim :: action ( editor, cx, Vim :: helix_insert) ;
2830 Vim :: action ( editor, cx, Vim :: helix_append) ;
31+ Vim :: action ( editor, cx, Vim :: helix_yank) ;
2932}
3033
3134impl Vim {
@@ -310,6 +313,47 @@ impl Vim {
310313 }
311314 }
312315
316+ pub fn helix_yank ( & mut self , _: & HelixYank , window : & mut Window , cx : & mut Context < Self > ) {
317+ self . update_editor ( cx, |vim, editor, cx| {
318+ let has_selection = editor
319+ . selections
320+ . all_adjusted ( cx)
321+ . iter ( )
322+ . any ( |selection| !selection. is_empty ( ) ) ;
323+
324+ if !has_selection {
325+ // If no selection, expand to current character (like 'v' does)
326+ editor. change_selections ( Default :: default ( ) , window, cx, |s| {
327+ s. move_with ( |map, selection| {
328+ let head = selection. head ( ) ;
329+ let new_head = movement:: saturating_right ( map, head) ;
330+ selection. set_tail ( head, SelectionGoal :: None ) ;
331+ selection. set_head ( new_head, SelectionGoal :: None ) ;
332+ } ) ;
333+ } ) ;
334+ vim. yank_selections_content (
335+ editor,
336+ crate :: motion:: MotionKind :: Exclusive ,
337+ window,
338+ cx,
339+ ) ;
340+ editor. change_selections ( Default :: default ( ) , window, cx, |s| {
341+ s. move_with ( |_map, selection| {
342+ selection. collapse_to ( selection. start , SelectionGoal :: None ) ;
343+ } ) ;
344+ } ) ;
345+ } else {
346+ // Yank the selection(s)
347+ vim. yank_selections_content (
348+ editor,
349+ crate :: motion:: MotionKind :: Exclusive ,
350+ window,
351+ cx,
352+ ) ;
353+ }
354+ } ) ;
355+ }
356+
313357 fn helix_insert ( & mut self , _: & HelixInsert , window : & mut Window , cx : & mut Context < Self > ) {
314358 self . start_recording ( cx) ;
315359 self . update_editor ( cx, |_, editor, cx| {
@@ -703,4 +747,29 @@ mod test {
703747
704748 cx. assert_state ( "«xxˇ»" , Mode :: HelixNormal ) ;
705749 }
750+
751+ #[ gpui:: test]
752+ async fn test_helix_yank ( cx : & mut gpui:: TestAppContext ) {
753+ let mut cx = VimTestContext :: new ( cx, true ) . await ;
754+ cx. enable_helix ( ) ;
755+
756+ // Test yanking current character with no selection
757+ cx. set_state ( "hello ˇworld" , Mode :: HelixNormal ) ;
758+ cx. simulate_keystrokes ( "y" ) ;
759+
760+ // Test cursor remains at the same position after yanking single character
761+ cx. assert_state ( "hello ˇworld" , Mode :: HelixNormal ) ;
762+ cx. shared_clipboard ( ) . assert_eq ( "w" ) ;
763+
764+ // Move cursor and yank another character
765+ cx. simulate_keystrokes ( "l" ) ;
766+ cx. simulate_keystrokes ( "y" ) ;
767+ cx. shared_clipboard ( ) . assert_eq ( "o" ) ;
768+
769+ // Test yanking with existing selection
770+ cx. set_state ( "hello «worlˇ»d" , Mode :: HelixNormal ) ;
771+ cx. simulate_keystrokes ( "y" ) ;
772+ cx. shared_clipboard ( ) . assert_eq ( "worl" ) ;
773+ cx. assert_state ( "hello «worlˇ»d" , Mode :: HelixNormal ) ;
774+ }
706775}
0 commit comments