@@ -21,6 +21,7 @@ pub(crate) struct GenericDisplayRow {
2121 pub match_indices : Option < Vec < usize > > , // indices to bold (char positions)
2222 pub is_current : bool ,
2323 pub description : Option < String > , // optional grey text after the name
24+ pub wrap_indent : Option < usize > , // optional indent for wrapped lines
2425}
2526
2627/// Compute a shared description-column start based on the widest visible name
@@ -47,13 +48,30 @@ fn compute_desc_col(
4748 desc_col
4849}
4950
51+ /// Determine how many spaces to indent wrapped lines for a row.
52+ fn wrap_indent ( row : & GenericDisplayRow , desc_col : usize , max_width : u16 ) -> usize {
53+ let max_indent = max_width. saturating_sub ( 1 ) as usize ;
54+ let indent = row. wrap_indent . unwrap_or_else ( || {
55+ if row. description . is_some ( ) {
56+ desc_col
57+ } else {
58+ 0
59+ }
60+ } ) ;
61+ indent. min ( max_indent)
62+ }
63+
5064/// Build the full display line for a row with the description padded to start
5165/// at `desc_col`. Applies fuzzy-match bolding when indices are present and
5266/// dims the description.
5367fn build_full_line ( row : & GenericDisplayRow , desc_col : usize ) -> Line < ' static > {
5468 // Enforce single-line name: allow at most desc_col - 2 cells for name,
5569 // reserving two spaces before the description column.
56- let name_limit = desc_col. saturating_sub ( 2 ) ;
70+ let name_limit = row
71+ . description
72+ . as_ref ( )
73+ . map ( |_| desc_col. saturating_sub ( 2 ) )
74+ . unwrap_or ( usize:: MAX ) ;
5775
5876 let mut name_spans: Vec < Span > = Vec :: with_capacity ( row. name . len ( ) ) ;
5977 let mut used_width = 0usize ;
@@ -63,11 +81,12 @@ fn build_full_line(row: &GenericDisplayRow, desc_col: usize) -> Line<'static> {
6381 let mut idx_iter = idxs. iter ( ) . peekable ( ) ;
6482 for ( char_idx, ch) in row. name . chars ( ) . enumerate ( ) {
6583 let ch_w = UnicodeWidthChar :: width ( ch) . unwrap_or ( 0 ) ;
66- if used_width + ch_w > name_limit {
84+ let next_width = used_width. saturating_add ( ch_w) ;
85+ if next_width > name_limit {
6786 truncated = true ;
6887 break ;
6988 }
70- used_width += ch_w ;
89+ used_width = next_width ;
7190
7291 if idx_iter. peek ( ) . is_some_and ( |next| * * next == char_idx) {
7392 idx_iter. next ( ) ;
@@ -79,11 +98,12 @@ fn build_full_line(row: &GenericDisplayRow, desc_col: usize) -> Line<'static> {
7998 } else {
8099 for ch in row. name . chars ( ) {
81100 let ch_w = UnicodeWidthChar :: width ( ch) . unwrap_or ( 0 ) ;
82- if used_width + ch_w > name_limit {
101+ let next_width = used_width. saturating_add ( ch_w) ;
102+ if next_width > name_limit {
83103 truncated = true ;
84104 break ;
85105 }
86- used_width += ch_w ;
106+ used_width = next_width ;
87107 name_spans. push ( ch. to_string ( ) . into ( ) ) ;
88108 }
89109 }
@@ -161,24 +181,7 @@ pub(crate) fn render_rows(
161181 break ;
162182 }
163183
164- let GenericDisplayRow {
165- name,
166- match_indices,
167- display_shortcut,
168- is_current : _is_current,
169- description,
170- } = row;
171-
172- let mut full_line = build_full_line (
173- & GenericDisplayRow {
174- name : name. clone ( ) ,
175- match_indices : match_indices. clone ( ) ,
176- display_shortcut : * display_shortcut,
177- is_current : * _is_current,
178- description : description. clone ( ) ,
179- } ,
180- desc_col,
181- ) ;
184+ let mut full_line = build_full_line ( row, desc_col) ;
182185 if Some ( i) == state. selected_idx {
183186 // Match previous behavior: cyan + bold for the selected row.
184187 // Reset the style first to avoid inheriting dim from keyboard shortcuts.
@@ -190,9 +193,10 @@ pub(crate) fn render_rows(
190193 // Wrap with subsequent indent aligned to the description column.
191194 use crate :: wrapping:: RtOptions ;
192195 use crate :: wrapping:: word_wrap_line;
196+ let continuation_indent = wrap_indent ( row, desc_col, area. width ) ;
193197 let options = RtOptions :: new ( area. width as usize )
194198 . initial_indent ( Line :: from ( "" ) )
195- . subsequent_indent ( Line :: from ( " " . repeat ( desc_col ) ) ) ;
199+ . subsequent_indent ( Line :: from ( " " . repeat ( continuation_indent ) ) ) ;
196200 let wrapped = word_wrap_line ( & full_line, options) ;
197201
198202 // Render the wrapped lines.
@@ -256,9 +260,10 @@ pub(crate) fn measure_rows_height(
256260 . map ( |( _, r) | r)
257261 {
258262 let full_line = build_full_line ( row, desc_col) ;
263+ let continuation_indent = wrap_indent ( row, desc_col, content_width) ;
259264 let opts = RtOptions :: new ( content_width as usize )
260265 . initial_indent ( Line :: from ( "" ) )
261- . subsequent_indent ( Line :: from ( " " . repeat ( desc_col ) ) ) ;
266+ . subsequent_indent ( Line :: from ( " " . repeat ( continuation_indent ) ) ) ;
262267 total = total. saturating_add ( word_wrap_line ( & full_line, opts) . len ( ) as u16 ) ;
263268 }
264269 total. max ( 1 )
0 commit comments