@@ -269,6 +269,10 @@ pub struct Picker<T: 'static + Send + Sync, D: 'static> {
269269    /// An event handler for syntax highlighting the currently previewed file. 
270270preview_highlight_handler :  Sender < Arc < Path > > , 
271271    dynamic_query_handler :  Option < Sender < DynamicQueryChange > > , 
272+ 
273+     preview_scroll_offset :  ( Direction ,  usize ) , 
274+     preview_height :  u16 , 
275+     cursor_picker :  u32 , 
272276} 
273277
274278impl < T :  ' static  + Send  + Sync ,  D :  ' static  + Send  + Sync >  Picker < T ,  D >  { 
@@ -391,6 +395,9 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
391395            file_fn :  None , 
392396            preview_highlight_handler :  PreviewHighlightHandler :: < T ,  D > :: default ( ) . spawn ( ) , 
393397            dynamic_query_handler :  None , 
398+             preview_scroll_offset :  ( Direction :: Forward ,  0 ) , 
399+             preview_height :  0 , 
400+             cursor_picker :  0 , 
394401        } 
395402    } 
396403
@@ -452,6 +459,44 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
452459        self 
453460    } 
454461
462+     /// Moves the picker file preview by a number of lines, either down (`Forward`) or up (`Backward`) 
463+ fn  move_preview_by ( & mut  self ,  amount :  usize ,  move_direction :  Direction )  { 
464+         let  ( current_scroll_direction,  current_scroll_offset)  = self . preview_scroll_offset ; 
465+ 
466+         match  move_direction { 
467+             Direction :: Backward  => match  current_scroll_direction { 
468+                 Direction :: Backward  => { 
469+                     self . preview_scroll_offset . 1  = current_scroll_offset. saturating_add ( amount) ; 
470+                 } 
471+                 Direction :: Forward  => { 
472+                     if  let  Some ( change)  = current_scroll_offset. checked_sub ( amount)  { 
473+                         self . preview_scroll_offset . 1  = change; 
474+                     }  else  { 
475+                         self . preview_scroll_offset  = ( 
476+                             Direction :: Backward , 
477+                             amount. saturating_sub ( current_scroll_offset) , 
478+                         ) ; 
479+                     } 
480+                 } 
481+             } , 
482+             Direction :: Forward  => match  current_scroll_direction { 
483+                 Direction :: Backward  => { 
484+                     if  let  Some ( change)  = current_scroll_offset. checked_sub ( amount)  { 
485+                         self . preview_scroll_offset . 1  = change; 
486+                     }  else  { 
487+                         self . preview_scroll_offset  = ( 
488+                             Direction :: Forward , 
489+                             amount. saturating_sub ( current_scroll_offset) , 
490+                         ) ; 
491+                     } 
492+                 } 
493+                 Direction :: Forward  => { 
494+                     self . preview_scroll_offset . 1  = current_scroll_offset. saturating_add ( amount) ; 
495+                 } 
496+             } , 
497+         } ; 
498+     } 
499+ 
455500    /// Move the cursor by a number of lines, either down (`Forward`) or up (`Backward`) 
456501pub  fn  move_by ( & mut  self ,  amount :  u32 ,  direction :  Direction )  { 
457502        let  len = self . matcher . snapshot ( ) . matched_item_count ( ) ; 
@@ -890,6 +935,15 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
890935        let  inner = inner. inner ( margin) ; 
891936        BLOCK . render ( area,  surface) ; 
892937
938+         let  mut  preview_scroll_offset = self . preview_scroll_offset ; 
939+ 
940+         // Reset preview scroll if cursor moved 
941+         let  cursor_position = self . cursor_picker ; 
942+         if  self . cursor  != cursor_position { 
943+             preview_scroll_offset = ( Direction :: Forward ,  0 ) ; 
944+             self . cursor_picker  = self . cursor ; 
945+         } 
946+ 
893947        if  let  Some ( ( preview,  range) )  = self . get_preview ( cx. editor )  { 
894948            let  doc = match  preview. document ( )  { 
895949                Some ( doc) 
@@ -923,6 +977,7 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
923977                    return ; 
924978                } 
925979            } ; 
980+             let  doc_height = doc. text ( ) . len_lines ( ) ; 
926981
927982            let  mut  offset = ViewPosition :: default ( ) ; 
928983            if  let  Some ( ( start_line,  end_line) )  = range { 
@@ -951,6 +1006,28 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
9511006                } 
9521007            } 
9531008
1009+             let  mut  current_line = doc. text ( ) . slice ( ..) . char_to_line ( offset. anchor ) ; 
1010+ 
1011+             preview_scroll_offset. 1  = match  preview_scroll_offset. 0  { 
1012+                 Direction :: Backward  => preview_scroll_offset. 1 . min ( current_line) , 
1013+                 Direction :: Forward  => preview_scroll_offset. 1 . min ( 
1014+                     doc_height
1015+                         . saturating_sub ( current_line) 
1016+                         . saturating_sub ( inner. height  as  usize ) , 
1017+                 ) , 
1018+             } ; 
1019+ 
1020+             offset. anchor  = match  preview_scroll_offset. 0  { 
1021+                 Direction :: Backward  => doc
1022+                     . text ( ) 
1023+                     . slice ( ..) 
1024+                     . line_to_char ( current_line. saturating_sub ( preview_scroll_offset. 1 ) ) , 
1025+                 Direction :: Forward  => doc
1026+                     . text ( ) 
1027+                     . slice ( ..) 
1028+                     . line_to_char ( current_line. saturating_add ( preview_scroll_offset. 1 ) ) , 
1029+             } ; 
1030+ 
9541031            let  loader = cx. editor . syn_loader . load ( ) ; 
9551032
9561033            let  syntax_highlighter =
@@ -985,6 +1062,8 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
9851062                decorations. add_decoration ( draw_highlight) ; 
9861063            } 
9871064
1065+             current_line = doc. text ( ) . slice ( ..) . char_to_line ( offset. anchor ) ; 
1066+ 
9881067            render_document ( 
9891068                surface, 
9901069                inner, 
@@ -997,6 +1076,39 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
9971076                & cx. editor . theme , 
9981077                decorations, 
9991078            ) ; 
1079+ 
1080+             self . preview_scroll_offset  = preview_scroll_offset; 
1081+ 
1082+             let  win_height = inner. height  as  usize ; 
1083+             let  len = doc_height; 
1084+             let  fits = len <= win_height; 
1085+             let  scroll = current_line; 
1086+             let  scroll_style = cx. editor . theme . get ( "ui.menu.scroll" ) ; 
1087+ 
1088+             const  fn  div_ceil ( a :  usize ,  b :  usize )  -> usize  { 
1089+                 ( a + b - 1 )  / b
1090+             } 
1091+ 
1092+             if  !fits { 
1093+                 let  scroll_height = div_ceil ( win_height. pow ( 2 ) ,  len) . min ( win_height) ; 
1094+                 let  scroll_line = ( win_height - scroll_height)  *  scroll
1095+                     / std:: cmp:: max ( 1 ,  len. saturating_sub ( win_height) ) ; 
1096+ 
1097+                 let  mut  cell; 
1098+                 for  i in  0 ..win_height { 
1099+                     cell = & mut  surface[ ( inner. right ( )  - 1 ,  inner. top ( )  + i as  u16 ) ] ; 
1100+ 
1101+                     cell. set_symbol ( "▐" ) ;  // right half block 
1102+ 
1103+                     if  scroll_line <= i && i < scroll_line + scroll_height { 
1104+                         // Draw scroll thumb 
1105+                         cell. set_fg ( scroll_style. fg . unwrap_or ( helix_view:: theme:: Color :: Reset ) ) ; 
1106+                     }  else  { 
1107+                         // Draw scroll track 
1108+                         cell. set_fg ( scroll_style. bg . unwrap_or ( helix_view:: theme:: Color :: Reset ) ) ; 
1109+                     } 
1110+                 } 
1111+             } 
10001112        } 
10011113    } 
10021114} 
@@ -1068,10 +1180,10 @@ impl<I: 'static + Send + Sync, D: 'static + Send + Sync> Component for Picker<I,
10681180            key ! ( Tab )  | key ! ( Down )  | ctrl ! ( 'n' )  => { 
10691181                self . move_by ( 1 ,  Direction :: Forward ) ; 
10701182            } 
1071-             key ! ( PageDown )  | ctrl ! ( 'd' )  => { 
1183+             key ! ( PageDown )  | ctrl ! ( 'd' )  if  ! self . show_preview   => { 
10721184                self . page_down ( ) ; 
10731185            } 
1074-             key ! ( PageUp )  | ctrl ! ( 'u' )  => { 
1186+             key ! ( PageUp )  | ctrl ! ( 'u' )  if  ! self . show_preview   => { 
10751187                self . page_up ( ) ; 
10761188            } 
10771189            key ! ( Home )  => { 
@@ -1136,6 +1248,36 @@ impl<I: 'static + Send + Sync, D: 'static + Send + Sync> Component for Picker<I,
11361248            ctrl ! ( 't' )  => { 
11371249                self . toggle_preview ( ) ; 
11381250            } 
1251+             alt ! ( 'k' )  | shift ! ( Up )  if  self . show_preview  => { 
1252+                 self . move_preview_by ( 
1253+                     ctx. editor . config ( ) . scroll_lines . unsigned_abs ( ) , 
1254+                     Direction :: Backward , 
1255+                 ) ; 
1256+             } 
1257+             alt ! ( 'j' )  | shift ! ( Down )  if  self . show_preview  => { 
1258+                 self . move_preview_by ( 
1259+                     ctx. editor . config ( ) . scroll_lines . unsigned_abs ( ) , 
1260+                     Direction :: Forward , 
1261+                 ) ; 
1262+             } 
1263+             alt ! ( 'u' )  if  self . show_preview  => { 
1264+                 self . move_preview_by ( 
1265+                     self . preview_height . saturating_div ( 2 )  as  usize , 
1266+                     Direction :: Backward , 
1267+                 ) ; 
1268+             } 
1269+             alt ! ( 'd' )  if  self . show_preview  => { 
1270+                 self . move_preview_by ( 
1271+                     self . preview_height . saturating_div ( 2 )  as  usize , 
1272+                     Direction :: Forward , 
1273+                 ) ; 
1274+             } 
1275+             key ! ( PageUp )  | alt ! ( 'b' )  if  self . show_preview  => { 
1276+                 self . move_preview_by ( self . preview_height  as  usize ,  Direction :: Backward ) ; 
1277+             } 
1278+             key ! ( PageDown )  | alt ! ( 'f' )  if  self . show_preview  => { 
1279+                 self . move_preview_by ( self . preview_height  as  usize ,  Direction :: Forward ) ; 
1280+             } 
11391281            _ => { 
11401282                self . prompt_handle_event ( event,  ctx) ; 
11411283            } 
@@ -1165,6 +1307,8 @@ impl<I: 'static + Send + Sync, D: 'static + Send + Sync> Component for Picker<I,
11651307
11661308    fn  required_size ( & mut  self ,  ( width,  height) :  ( u16 ,  u16 ) )  -> Option < ( u16 ,  u16 ) >  { 
11671309        self . completion_height  = height. saturating_sub ( 4  + self . header_height ( ) ) ; 
1310+         self . preview_height  = height. saturating_sub ( 2 ) ; 
1311+ 
11681312        Some ( ( width,  height) ) 
11691313    } 
11701314
0 commit comments