@@ -15,8 +15,8 @@ use language::{CharClassifier, CharKind, Point};
1515use search:: { BufferSearchBar , SearchOptions } ;
1616use settings:: Settings ;
1717use text:: { Bias , SelectionGoal } ;
18- use workspace:: searchable;
1918use workspace:: searchable:: FilteredSearchRange ;
19+ use workspace:: searchable:: { self , Direction } ;
2020
2121use crate :: motion:: { self , MotionKind } ;
2222use crate :: state:: SearchState ;
@@ -52,6 +52,10 @@ actions!(
5252 HelixSubstitute ,
5353 /// Delete the selection and enter edit mode, without yanking the selection.
5454 HelixSubstituteNoYank ,
55+ /// Delete the selection and enter edit mode.
56+ HelixSelectNext ,
57+ /// Delete the selection and enter edit mode, without yanking the selection.
58+ HelixSelectPrevious ,
5559 ]
5660) ;
5761
@@ -74,6 +78,8 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
7478 } ) ;
7579 Vim :: action ( editor, cx, Vim :: helix_substitute) ;
7680 Vim :: action ( editor, cx, Vim :: helix_substitute_no_yank) ;
81+ Vim :: action ( editor, cx, Vim :: helix_select_next) ;
82+ Vim :: action ( editor, cx, Vim :: helix_select_previous) ;
7783}
7884
7985impl Vim {
@@ -97,6 +103,11 @@ impl Vim {
97103 self . update_editor ( cx, |_, editor, cx| {
98104 let text_layout_details = editor. text_layout_details ( window) ;
99105 editor. change_selections ( Default :: default ( ) , window, cx, |s| {
106+ if let Motion :: ZedSearchResult { new_selections, .. } = & motion {
107+ s. select_anchor_ranges ( new_selections. clone ( ) ) ;
108+ return ;
109+ } ;
110+
100111 s. move_with ( |map, selection| {
101112 let current_head = selection. head ( ) ;
102113
@@ -664,6 +675,68 @@ impl Vim {
664675 ) {
665676 self . do_helix_substitute ( false , window, cx) ;
666677 }
678+
679+ fn helix_select_next (
680+ & mut self ,
681+ _: & HelixSelectNext ,
682+ window : & mut Window ,
683+ cx : & mut Context < Self > ,
684+ ) {
685+ self . do_helix_select ( Direction :: Next , window, cx) ;
686+ }
687+
688+ fn helix_select_previous (
689+ & mut self ,
690+ _: & HelixSelectPrevious ,
691+ window : & mut Window ,
692+ cx : & mut Context < Self > ,
693+ ) {
694+ self . do_helix_select ( Direction :: Prev , window, cx) ;
695+ }
696+
697+ fn do_helix_select (
698+ & mut self ,
699+ direction : searchable:: Direction ,
700+ window : & mut Window ,
701+ cx : & mut Context < Self > ,
702+ ) {
703+ let Some ( pane) = self . pane ( window, cx) else {
704+ return ;
705+ } ;
706+ let count = Vim :: take_count ( cx) . unwrap_or ( 1 ) ;
707+ Vim :: take_forced_motion ( cx) ;
708+ let prior_selections = self . editor_selections ( window, cx) ;
709+
710+ let success = pane. update ( cx, |pane, cx| {
711+ let Some ( search_bar) = pane. toolbar ( ) . read ( cx) . item_of_type :: < BufferSearchBar > ( ) else {
712+ return false ;
713+ } ;
714+ search_bar. update ( cx, |search_bar, cx| {
715+ if !search_bar. has_active_match ( ) || !search_bar. show ( window, cx) {
716+ return false ;
717+ }
718+ search_bar. select_match ( direction, count, window, cx) ;
719+ true
720+ } )
721+ } ) ;
722+
723+ if !success {
724+ return ;
725+ }
726+ if self . mode == Mode :: HelixSelect {
727+ self . update_editor ( cx, |_vim, editor, cx| {
728+ let snapshot = editor. snapshot ( window, cx) ;
729+ editor. change_selections ( SelectionEffects :: default ( ) , window, cx, |s| {
730+ s. select_anchor_ranges (
731+ prior_selections
732+ . iter ( )
733+ . cloned ( )
734+ . chain ( s. all_anchors ( & snapshot) . iter ( ) . map ( |s| s. range ( ) ) ) ,
735+ ) ;
736+ } )
737+ } ) ;
738+ }
739+ }
667740}
668741
669742#[ cfg( test) ]
@@ -1278,6 +1351,24 @@ mod test {
12781351 cx. assert_state ( "«one ˇ»two" , Mode :: HelixSelect ) ;
12791352 }
12801353
1354+ #[ gpui:: test]
1355+ async fn test_exit_visual_mode ( cx : & mut gpui:: TestAppContext ) {
1356+ let mut cx = VimTestContext :: new ( cx, true ) . await ;
1357+
1358+ cx. set_state ( "ˇone two" , Mode :: Normal ) ;
1359+ cx. simulate_keystrokes ( "v w" ) ;
1360+ cx. assert_state ( "«one tˇ»wo" , Mode :: Visual ) ;
1361+ cx. simulate_keystrokes ( "escape" ) ;
1362+ cx. assert_state ( "one ˇtwo" , Mode :: Normal ) ;
1363+
1364+ cx. enable_helix ( ) ;
1365+ cx. set_state ( "ˇone two" , Mode :: HelixNormal ) ;
1366+ cx. simulate_keystrokes ( "v w" ) ;
1367+ cx. assert_state ( "«one ˇ»two" , Mode :: HelixSelect ) ;
1368+ cx. simulate_keystrokes ( "escape" ) ;
1369+ cx. assert_state ( "«one ˇ»two" , Mode :: HelixNormal ) ;
1370+ }
1371+
12811372 #[ gpui:: test]
12821373 async fn test_helix_select_regex ( cx : & mut gpui:: TestAppContext ) {
12831374 let mut cx = VimTestContext :: new ( cx, true ) . await ;
@@ -1297,9 +1388,47 @@ mod test {
12971388 cx. simulate_keystrokes ( "enter" ) ;
12981389 cx. assert_state ( "«oneˇ» two «oneˇ»" , Mode :: HelixNormal ) ;
12991390
1300- cx. set_state ( "ˇone two one" , Mode :: HelixNormal ) ;
1301- cx. simulate_keystrokes ( "s o n e enter" ) ;
1302- cx. assert_state ( "ˇone two one" , Mode :: HelixNormal ) ;
1391+ // TODO: change "search_in_selection" to not perform any search when in helix select mode with no selection
1392+ // cx.set_state("ˇstuff one two one", Mode::HelixNormal);
1393+ // cx.simulate_keystrokes("s o n e enter");
1394+ // cx.assert_state("ˇstuff one two one", Mode::HelixNormal);
1395+ }
1396+
1397+ #[ gpui:: test]
1398+ async fn test_helix_select_next_match ( cx : & mut gpui:: TestAppContext ) {
1399+ let mut cx = VimTestContext :: new ( cx, true ) . await ;
1400+
1401+ cx. set_state ( "ˇhello two one two one two one" , Mode :: Visual ) ;
1402+ cx. simulate_keystrokes ( "/ o n e" ) ;
1403+ cx. simulate_keystrokes ( "enter" ) ;
1404+ cx. simulate_keystrokes ( "n n" ) ;
1405+ cx. assert_state ( "«hello two one two one two oˇ»ne" , Mode :: Visual ) ;
1406+
1407+ cx. set_state ( "ˇhello two one two one two one" , Mode :: Normal ) ;
1408+ cx. simulate_keystrokes ( "/ o n e" ) ;
1409+ cx. simulate_keystrokes ( "enter" ) ;
1410+ cx. simulate_keystrokes ( "n n" ) ;
1411+ cx. assert_state ( "hello two one two one two ˇone" , Mode :: Normal ) ;
1412+
1413+ cx. set_state ( "ˇhello two one two one two one" , Mode :: Normal ) ;
1414+ cx. simulate_keystrokes ( "/ o n e" ) ;
1415+ cx. simulate_keystrokes ( "enter" ) ;
1416+ cx. simulate_keystrokes ( "n g n g n" ) ;
1417+ cx. assert_state ( "hello two one two «one two oneˇ»" , Mode :: Visual ) ;
1418+
1419+ cx. enable_helix ( ) ;
1420+
1421+ cx. set_state ( "ˇhello two one two one two one" , Mode :: HelixNormal ) ;
1422+ cx. simulate_keystrokes ( "/ o n e" ) ;
1423+ cx. simulate_keystrokes ( "enter" ) ;
1424+ cx. simulate_keystrokes ( "n n" ) ;
1425+ cx. assert_state ( "hello two one two one two «oneˇ»" , Mode :: HelixNormal ) ;
1426+
1427+ cx. set_state ( "ˇhello two one two one two one" , Mode :: HelixSelect ) ;
1428+ cx. simulate_keystrokes ( "/ o n e" ) ;
1429+ cx. simulate_keystrokes ( "enter" ) ;
1430+ cx. simulate_keystrokes ( "n n" ) ;
1431+ cx. assert_state ( "hello two «oneˇ» two «oneˇ» two «oneˇ»" , Mode :: HelixSelect ) ;
13031432 }
13041433
13051434 #[ gpui:: test]
0 commit comments