@@ -268,6 +268,10 @@ pub struct Picker<T: 'static + Send + Sync, D: 'static> {
268268 /// An event handler for syntax highlighting the currently previewed file.
269269 preview_highlight_handler : Sender < Arc < Path > > ,
270270 dynamic_query_handler : Option < Sender < DynamicQueryChange > > ,
271+
272+ preview_scroll_offset : ( Direction , usize ) ,
273+ preview_height : u16 ,
274+ cursor_picker : u32 ,
271275}
272276
273277impl < T : ' static + Send + Sync , D : ' static + Send + Sync > Picker < T , D > {
@@ -389,6 +393,9 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
389393 file_fn : None ,
390394 preview_highlight_handler : PreviewHighlightHandler :: < T , D > :: default ( ) . spawn ( ) ,
391395 dynamic_query_handler : None ,
396+ preview_scroll_offset : ( Direction :: Forward , 0 ) ,
397+ preview_height : 0 ,
398+ cursor_picker : 0 ,
392399 }
393400 }
394401
@@ -440,6 +447,44 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
440447 self
441448 }
442449
450+ /// Moves the picker file preview by a number of lines, either down (`Forward`) or up (`Backward`)
451+ fn move_preview_by ( & mut self , amount : usize , move_direction : Direction ) {
452+ let ( current_scroll_direction, current_scroll_offset) = self . preview_scroll_offset ;
453+
454+ match move_direction {
455+ Direction :: Backward => match current_scroll_direction {
456+ Direction :: Backward => {
457+ self . preview_scroll_offset . 1 = current_scroll_offset. saturating_add ( amount) ;
458+ }
459+ Direction :: Forward => {
460+ if let Some ( change) = current_scroll_offset. checked_sub ( amount) {
461+ self . preview_scroll_offset . 1 = change;
462+ } else {
463+ self . preview_scroll_offset = (
464+ Direction :: Backward ,
465+ amount. saturating_sub ( current_scroll_offset) ,
466+ ) ;
467+ }
468+ }
469+ } ,
470+ Direction :: Forward => match current_scroll_direction {
471+ Direction :: Backward => {
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 :: Forward ,
477+ amount. saturating_sub ( current_scroll_offset) ,
478+ ) ;
479+ }
480+ }
481+ Direction :: Forward => {
482+ self . preview_scroll_offset . 1 = current_scroll_offset. saturating_add ( amount) ;
483+ }
484+ } ,
485+ } ;
486+ }
487+
443488 /// Move the cursor by a number of lines, either down (`Forward`) or up (`Backward`)
444489 pub fn move_by ( & mut self , amount : u32 , direction : Direction ) {
445490 let len = self . matcher . snapshot ( ) . matched_item_count ( ) ;
@@ -872,6 +917,15 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
872917 let inner = inner. inner ( margin) ;
873918 BLOCK . render ( area, surface) ;
874919
920+ let mut preview_scroll_offset = self . preview_scroll_offset ;
921+
922+ // Reset preview scroll if cursor moved
923+ let cursor_position = self . cursor_picker ;
924+ if self . cursor != cursor_position {
925+ preview_scroll_offset = ( Direction :: Forward , 0 ) ;
926+ self . cursor_picker = self . cursor ;
927+ }
928+
875929 if let Some ( ( preview, range) ) = self . get_preview ( cx. editor ) {
876930 let doc = match preview. document ( ) {
877931 Some ( doc)
@@ -905,6 +959,7 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
905959 return ;
906960 }
907961 } ;
962+ let doc_height = doc. text ( ) . len_lines ( ) ;
908963
909964 let mut offset = ViewPosition :: default ( ) ;
910965 if let Some ( ( start_line, end_line) ) = range {
@@ -933,6 +988,28 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
933988 }
934989 }
935990
991+ let mut current_line = doc. text ( ) . slice ( ..) . char_to_line ( offset. anchor ) ;
992+
993+ preview_scroll_offset. 1 = match preview_scroll_offset. 0 {
994+ Direction :: Backward => preview_scroll_offset. 1 . min ( current_line) ,
995+ Direction :: Forward => preview_scroll_offset. 1 . min (
996+ doc_height
997+ . saturating_sub ( current_line)
998+ . saturating_sub ( inner. height as usize ) ,
999+ ) ,
1000+ } ;
1001+
1002+ offset. anchor = match preview_scroll_offset. 0 {
1003+ Direction :: Backward => doc
1004+ . text ( )
1005+ . slice ( ..)
1006+ . line_to_char ( current_line. saturating_sub ( preview_scroll_offset. 1 ) ) ,
1007+ Direction :: Forward => doc
1008+ . text ( )
1009+ . slice ( ..)
1010+ . line_to_char ( current_line. saturating_add ( preview_scroll_offset. 1 ) ) ,
1011+ } ;
1012+
9361013 let syntax_highlights = EditorView :: doc_syntax_highlights (
9371014 doc,
9381015 offset. anchor ,
@@ -970,6 +1047,8 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
9701047 decorations. add_decoration ( draw_highlight) ;
9711048 }
9721049
1050+ current_line = doc. text ( ) . slice ( ..) . char_to_line ( offset. anchor ) ;
1051+
9731052 render_document (
9741053 surface,
9751054 inner,
@@ -982,6 +1061,39 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
9821061 & cx. editor . theme ,
9831062 decorations,
9841063 ) ;
1064+
1065+ self . preview_scroll_offset = preview_scroll_offset;
1066+
1067+ let win_height = inner. height as usize ;
1068+ let len = doc_height;
1069+ let fits = len <= win_height;
1070+ let scroll = current_line;
1071+ let scroll_style = cx. editor . theme . get ( "ui.menu.scroll" ) ;
1072+
1073+ const fn div_ceil ( a : usize , b : usize ) -> usize {
1074+ ( a + b - 1 ) / b
1075+ }
1076+
1077+ if !fits {
1078+ let scroll_height = div_ceil ( win_height. pow ( 2 ) , len) . min ( win_height) ;
1079+ let scroll_line = ( win_height - scroll_height) * scroll
1080+ / std:: cmp:: max ( 1 , len. saturating_sub ( win_height) ) ;
1081+
1082+ let mut cell;
1083+ for i in 0 ..win_height {
1084+ cell = & mut surface[ ( inner. right ( ) - 1 , inner. top ( ) + i as u16 ) ] ;
1085+
1086+ cell. set_symbol ( "▐" ) ; // right half block
1087+
1088+ if scroll_line <= i && i < scroll_line + scroll_height {
1089+ // Draw scroll thumb
1090+ cell. set_fg ( scroll_style. fg . unwrap_or ( helix_view:: theme:: Color :: Reset ) ) ;
1091+ } else {
1092+ // Draw scroll track
1093+ cell. set_fg ( scroll_style. bg . unwrap_or ( helix_view:: theme:: Color :: Reset ) ) ;
1094+ }
1095+ }
1096+ }
9851097 }
9861098 }
9871099}
@@ -1053,10 +1165,10 @@ impl<I: 'static + Send + Sync, D: 'static + Send + Sync> Component for Picker<I,
10531165 key ! ( Tab ) | key ! ( Down ) | ctrl ! ( 'n' ) => {
10541166 self . move_by ( 1 , Direction :: Forward ) ;
10551167 }
1056- key ! ( PageDown ) | ctrl ! ( 'd' ) => {
1168+ key ! ( PageDown ) | ctrl ! ( 'd' ) if ! self . show_preview => {
10571169 self . page_down ( ) ;
10581170 }
1059- key ! ( PageUp ) | ctrl ! ( 'u' ) => {
1171+ key ! ( PageUp ) | ctrl ! ( 'u' ) if ! self . show_preview => {
10601172 self . page_up ( ) ;
10611173 }
10621174 key ! ( Home ) => {
@@ -1121,6 +1233,36 @@ impl<I: 'static + Send + Sync, D: 'static + Send + Sync> Component for Picker<I,
11211233 ctrl ! ( 't' ) => {
11221234 self . toggle_preview ( ) ;
11231235 }
1236+ alt ! ( 'k' ) | shift ! ( Up ) if self . show_preview => {
1237+ self . move_preview_by (
1238+ ctx. editor . config ( ) . scroll_lines . unsigned_abs ( ) ,
1239+ Direction :: Backward ,
1240+ ) ;
1241+ }
1242+ alt ! ( 'j' ) | shift ! ( Down ) if self . show_preview => {
1243+ self . move_preview_by (
1244+ ctx. editor . config ( ) . scroll_lines . unsigned_abs ( ) ,
1245+ Direction :: Forward ,
1246+ ) ;
1247+ }
1248+ alt ! ( 'u' ) if self . show_preview => {
1249+ self . move_preview_by (
1250+ self . preview_height . saturating_div ( 2 ) as usize ,
1251+ Direction :: Backward ,
1252+ ) ;
1253+ }
1254+ alt ! ( 'd' ) if self . show_preview => {
1255+ self . move_preview_by (
1256+ self . preview_height . saturating_div ( 2 ) as usize ,
1257+ Direction :: Forward ,
1258+ ) ;
1259+ }
1260+ key ! ( PageUp ) | alt ! ( 'b' ) if self . show_preview => {
1261+ self . move_preview_by ( self . preview_height as usize , Direction :: Backward ) ;
1262+ }
1263+ key ! ( PageDown ) | alt ! ( 'f' ) if self . show_preview => {
1264+ self . move_preview_by ( self . preview_height as usize , Direction :: Forward ) ;
1265+ }
11241266 _ => {
11251267 self . prompt_handle_event ( event, ctx) ;
11261268 }
@@ -1150,6 +1292,8 @@ impl<I: 'static + Send + Sync, D: 'static + Send + Sync> Component for Picker<I,
11501292
11511293 fn required_size ( & mut self , ( width, height) : ( u16 , u16 ) ) -> Option < ( u16 , u16 ) > {
11521294 self . completion_height = height. saturating_sub ( 4 + self . header_height ( ) ) ;
1295+ self . preview_height = height. saturating_sub ( 2 ) ;
1296+
11531297 Some ( ( width, height) )
11541298 }
11551299
0 commit comments