1+ use std:: borrow:: Cow ;
2+
13use super :: { Menu , MenuBuilder , MenuEvent , MenuSettings } ;
24use crate :: {
35 core_editor:: Editor ,
46 menu_functions:: {
5- can_partially_complete, completer_input, replace_in_buffer, style_suggestion,
7+ can_partially_complete, completer_input, floor_char_boundary, replace_in_buffer,
8+ style_suggestion,
69 } ,
710 painting:: Painter ,
811 Completer , Suggestion ,
912} ;
1013use nu_ansi_term:: ansi:: RESET ;
14+ use unicode_segmentation:: UnicodeSegmentation ;
1115use unicode_width:: UnicodeWidthStr ;
1216
1317/// The traversal direction of the menu
@@ -377,98 +381,70 @@ impl ColumnarMenu {
377381 & self ,
378382 suggestion : & Suggestion ,
379383 index : usize ,
380- empty_space : usize ,
381384 use_ansi_coloring : bool ,
382385 ) -> String {
386+ let selected = index == self . index ( ) ;
387+ let empty_space = self . get_width ( ) . saturating_sub ( suggestion. value . width ( ) ) ;
388+
383389 if use_ansi_coloring {
384- // strip quotes
390+ // TODO(ysthakur): let the user strip quotes, rather than doing it here
385391 let is_quote = |c : char | "`'\" " . contains ( c) ;
386392 let shortest_base = & self . working_details . shortest_base_string ;
387393 let shortest_base = shortest_base
388394 . strip_prefix ( is_quote)
389395 . unwrap_or ( shortest_base) ;
390- let match_len = shortest_base. chars ( ) . count ( ) ;
391396
392- let suggestion_style = suggestion. style . unwrap_or ( self . settings . color . text_style ) ;
397+ let match_indices = if let Some ( match_indices) = & suggestion. match_indices {
398+ Cow :: Borrowed ( match_indices)
399+ } else if let Some ( match_pos) = suggestion
400+ . value
401+ . to_lowercase ( )
402+ . find ( & shortest_base. to_lowercase ( ) )
403+ {
404+ // Highlight matched substring if one is found
405+ let match_len = shortest_base. graphemes ( true ) . count ( ) ;
406+ Cow :: Owned ( ( match_pos..match_pos + match_len) . collect ( ) )
407+ } else {
408+ // Don't highlight anything if no match
409+ Cow :: Owned ( vec ! [ ] )
410+ } ;
393411
394412 let left_text_size = self . longest_suggestion + self . default_details . col_padding ;
395- let right_text_size = self . get_width ( ) . saturating_sub ( left_text_size) ;
396-
397- let default_indices = ( 0 ..match_len) . collect ( ) ;
398- let match_indices = suggestion
399- . match_indices
400- . as_ref ( )
401- . unwrap_or ( & default_indices) ;
413+ let description_size = self . get_width ( ) . saturating_sub ( left_text_size) ;
414+ let padding = left_text_size. saturating_sub ( suggestion. value . len ( ) ) ;
402415
403- if index == self . index ( ) {
404- if let Some ( description) = & suggestion. description {
416+ let value_style = if selected {
417+ & self . settings . color . selected_text_style
418+ } else {
419+ & suggestion. style . unwrap_or ( self . settings . color . text_style )
420+ } ;
421+ let styled_value = style_suggestion (
422+ value_style. paint ( & suggestion. value ) . as_str ( ) ,
423+ match_indices. as_ref ( ) ,
424+ & self . settings . color . match_style ,
425+ ) ;
426+
427+ if let Some ( description) = & suggestion. description {
428+ let styled_desc = self . settings . color . description_style . paint (
429+ description
430+ . chars ( )
431+ . take ( description_size)
432+ . collect :: < String > ( )
433+ . replace ( '\n' , " " ) ,
434+ ) ;
435+ if selected {
405436 format ! (
406- "{:left_text_size$}{}{}{}{}{}" ,
407- style_suggestion(
408- & self
409- . settings
410- . color
411- . selected_text_style
412- . paint( & suggestion. value)
413- . to_string( ) ,
414- match_indices,
415- & self . settings. color. selected_match_style,
416- ) ,
417- self . settings. color. description_style. prefix( ) ,
437+ "{}{}{}{}" ,
438+ styled_value,
439+ " " . repeat( padding) ,
418440 self . settings. color. selected_text_style. prefix( ) ,
419- description
420- . chars( )
421- . take( right_text_size)
422- . collect:: <String >( )
423- . replace( '\n' , " " ) ,
424- RESET ,
441+ styled_desc,
425442 )
426443 } else {
427- format ! (
428- "{}{:>empty$}{}" ,
429- style_suggestion(
430- & self
431- . settings
432- . color
433- . selected_text_style
434- . paint( & suggestion. value)
435- . to_string( ) ,
436- match_indices,
437- & self . settings. color. selected_match_style,
438- ) ,
439- "" ,
440- empty = empty_space,
441- )
444+ format ! ( "{}{}{}" , styled_value, " " . repeat( padding) , styled_desc)
442445 }
443- } else if let Some ( description) = & suggestion. description {
444- format ! (
445- "{:left_text_size$}{}{}{}{}" ,
446- style_suggestion(
447- & suggestion_style. paint( & suggestion. value) . to_string( ) ,
448- match_indices,
449- & self . settings. color. match_style,
450- ) ,
451- self . settings. color. description_style. prefix( ) ,
452- description
453- . chars( )
454- . take( right_text_size)
455- . collect:: <String >( )
456- . replace( '\n' , " " ) ,
457- RESET ,
458- )
459446 } else {
460- format ! (
461- "{}{}{:>empty$}{}{}" ,
462- style_suggestion(
463- & suggestion_style. paint( & suggestion. value) . to_string( ) ,
464- match_indices,
465- & self . settings. color. match_style,
466- ) ,
467- self . settings. color. description_style. prefix( ) ,
468- "" ,
469- RESET ,
470- empty = empty_space,
471- )
447+ format ! ( "{}{:>empty$}" , styled_value, "" , empty = empty_space, )
472448 }
473449 } else {
474450 // If no ansi coloring is found, then the selection word is the line in uppercase
@@ -500,7 +476,7 @@ impl ColumnarMenu {
500476 )
501477 } ;
502478
503- if index == self . index ( ) {
479+ if selected {
504480 line. to_uppercase ( )
505481 } else {
506482 line
@@ -751,14 +727,7 @@ impl Menu for ColumnarMenu {
751727 . step_by ( num_rows)
752728 . take ( self . get_cols ( ) . into ( ) )
753729 . map ( |( index, suggestion) | {
754- let empty_space =
755- self . get_width ( ) . saturating_sub ( suggestion. value . width ( ) ) ;
756- self . create_string (
757- suggestion,
758- index,
759- empty_space,
760- use_ansi_coloring,
761- )
730+ self . create_string ( suggestion, index, use_ansi_coloring)
762731 } )
763732 . collect ( ) ;
764733 menu_string. push_str ( & row_string) ;
@@ -779,8 +748,6 @@ impl Menu for ColumnarMenu {
779748 // Correcting the enumerate index based on the number of skipped values
780749 let index = index + skip_values;
781750 let column = index % self . get_cols ( ) as usize ;
782- let empty_space =
783- self . get_width ( ) . saturating_sub ( suggestion. value . width ( ) ) ;
784751
785752 let end_of_line =
786753 if column == self . get_cols ( ) . saturating_sub ( 1 ) as usize {
@@ -790,12 +757,7 @@ impl Menu for ColumnarMenu {
790757 } ;
791758 format ! (
792759 "{}{}" ,
793- self . create_string(
794- suggestion,
795- index,
796- empty_space,
797- use_ansi_coloring
798- ) ,
760+ self . create_string( suggestion, index, use_ansi_coloring) ,
799761 end_of_line
800762 )
801763 } )
0 commit comments