@@ -266,10 +266,20 @@ impl BottomPaneView for ListSelectionView {
266266 match key_event {
267267 KeyEvent {
268268 code : KeyCode :: Up , ..
269+ }
270+ | KeyEvent {
271+ code : KeyCode :: Char ( 'p' ) ,
272+ modifiers : KeyModifiers :: CONTROL ,
273+ ..
269274 } => self . move_up ( ) ,
270275 KeyEvent {
271276 code : KeyCode :: Down ,
272277 ..
278+ }
279+ | KeyEvent {
280+ code : KeyCode :: Char ( 'n' ) ,
281+ modifiers : KeyModifiers :: CONTROL ,
282+ ..
273283 } => self . move_down ( ) ,
274284 KeyEvent {
275285 code : KeyCode :: Backspace ,
@@ -713,4 +723,80 @@ mod tests {
713723 render_lines_with_width( & view, 24 )
714724 ) ;
715725 }
726+
727+ #[ test]
728+ fn ctrl_n_moves_selection_down ( ) {
729+ let mut view = make_selection_view ( None ) ;
730+ // Initial selection is on first item (Read Only which is_current)
731+ let initial = render_lines ( & view) ;
732+ assert ! (
733+ initial. contains( "› 1. Read Only" ) ,
734+ "expected first item selected initially"
735+ ) ;
736+
737+ // Press Ctrl+n to move down
738+ view. handle_key_event ( KeyEvent :: new (
739+ KeyCode :: Char ( 'n' ) ,
740+ KeyModifiers :: CONTROL ,
741+ ) ) ;
742+ let after_ctrl_n = render_lines ( & view) ;
743+ assert ! (
744+ after_ctrl_n. contains( "› 2. Full Access" ) ,
745+ "expected second item selected after Ctrl+n"
746+ ) ;
747+ }
748+
749+ #[ test]
750+ fn ctrl_p_moves_selection_up ( ) {
751+ let mut view = make_selection_view ( None ) ;
752+ // Move down first so we can move up
753+ view. handle_key_event ( KeyEvent :: new ( KeyCode :: Down , KeyModifiers :: NONE ) ) ;
754+ let after_down = render_lines ( & view) ;
755+ assert ! (
756+ after_down. contains( "› 2. Full Access" ) ,
757+ "expected second item selected after Down"
758+ ) ;
759+
760+ // Press Ctrl+p to move up
761+ view. handle_key_event ( KeyEvent :: new (
762+ KeyCode :: Char ( 'p' ) ,
763+ KeyModifiers :: CONTROL ,
764+ ) ) ;
765+ let after_ctrl_p = render_lines ( & view) ;
766+ assert ! (
767+ after_ctrl_p. contains( "› 1. Read Only" ) ,
768+ "expected first item selected after Ctrl+p"
769+ ) ;
770+ }
771+
772+ #[ test]
773+ fn ctrl_n_and_ctrl_p_wrap_around ( ) {
774+ let mut view = make_selection_view ( None ) ;
775+ // Move to second item
776+ view. handle_key_event ( KeyEvent :: new (
777+ KeyCode :: Char ( 'n' ) ,
778+ KeyModifiers :: CONTROL ,
779+ ) ) ;
780+ // Move past last item - should wrap to first
781+ view. handle_key_event ( KeyEvent :: new (
782+ KeyCode :: Char ( 'n' ) ,
783+ KeyModifiers :: CONTROL ,
784+ ) ) ;
785+ let wrapped_forward = render_lines ( & view) ;
786+ assert ! (
787+ wrapped_forward. contains( "› 1. Read Only" ) ,
788+ "expected selection to wrap to first item"
789+ ) ;
790+
791+ // Move up from first item - should wrap to last
792+ view. handle_key_event ( KeyEvent :: new (
793+ KeyCode :: Char ( 'p' ) ,
794+ KeyModifiers :: CONTROL ,
795+ ) ) ;
796+ let wrapped_back = render_lines ( & view) ;
797+ assert ! (
798+ wrapped_back. contains( "› 2. Full Access" ) ,
799+ "expected selection to wrap to last item"
800+ ) ;
801+ }
716802}
0 commit comments