@@ -79,7 +79,7 @@ pub struct GridOptions {
7979#[ derive( PartialEq , Eq , Debug ) ]
8080struct Dimensions {
8181 /// The number of lines in the grid.
82- num_lines : usize ,
82+ num_rows : usize ,
8383
8484 /// The width of each column in the grid. The length of this vector serves
8585 /// as the number of columns.
@@ -113,23 +113,21 @@ impl<T: AsRef<str>> Grid<T> {
113113 pub fn new ( cells : Vec < T > , options : GridOptions ) -> Self {
114114 let widths: Vec < usize > = cells. iter ( ) . map ( |c| ansi_width ( c. as_ref ( ) ) ) . collect ( ) ;
115115 let widest_cell_width = widths. iter ( ) . copied ( ) . max ( ) . unwrap_or ( 0 ) ;
116- let width = options. width ;
117116
118117 let mut grid = Self {
119118 options,
120119 cells,
121120 widths,
122121 widest_cell_width,
123122 dimensions : Dimensions {
124- num_lines : 0 ,
123+ num_rows : 0 ,
125124 widths : Vec :: new ( ) ,
126125 } ,
127126 } ;
128127
129- grid. dimensions = grid. width_dimensions ( width) . unwrap_or ( Dimensions {
130- num_lines : grid. cells . len ( ) ,
131- widths : vec ! [ widest_cell_width] ,
132- } ) ;
128+ if !grid. cells . is_empty ( ) {
129+ grid. dimensions = grid. width_dimensions ( ) ;
130+ }
133131
134132 grid
135133 }
@@ -142,7 +140,7 @@ impl<T: AsRef<str>> Grid<T> {
142140
143141 /// The number of rows this display takes up.
144142 pub fn row_count ( & self ) -> usize {
145- self . dimensions . num_lines
143+ self . dimensions . num_rows
146144 }
147145
148146 /// The width of each column
@@ -174,99 +172,65 @@ impl<T: AsRef<str>> Grid<T> {
174172 }
175173
176174 Dimensions {
177- num_lines,
175+ num_rows : num_lines,
178176 widths : column_widths,
179177 }
180178 }
181179
182- fn theoretical_max_num_lines ( & self , maximum_width : usize ) -> usize {
183- // TODO: Make code readable / efficient.
184- let mut widths = self . widths . clone ( ) ;
185-
186- // Sort widths in reverse order
187- widths. sort_unstable_by ( |a, b| b. cmp ( a) ) ;
188-
189- let mut col_total_width_so_far = 0 ;
190- for ( i, & width) in widths. iter ( ) . enumerate ( ) {
191- let adjusted_width = if i == 0 {
192- width
193- } else {
194- width + self . options . filling . width ( )
180+ fn width_dimensions ( & mut self ) -> Dimensions {
181+ if self . cells . len ( ) == 1 {
182+ let cell_widths = self . widths [ 0 ] ;
183+ return Dimensions {
184+ num_rows : 1 ,
185+ widths : vec ! [ cell_widths] ,
195186 } ;
196- if col_total_width_so_far + adjusted_width <= maximum_width {
197- col_total_width_so_far += adjusted_width;
198- } else {
199- return div_ceil ( self . cells . len ( ) , i) ;
200- }
201187 }
202188
203- // If we make it to this point, we have exhausted all cells before
204- // reaching the maximum width; the theoretical max number of lines
205- // needed to display all cells is 1.
206- 1
207- }
208-
209- fn width_dimensions ( & self , maximum_width : usize ) -> Option < Dimensions > {
210- if self . widest_cell_width > maximum_width {
211- // Largest cell is wider than maximum width; it is impossible to fit.
212- return None ;
189+ // Calculate widest column size with separator.
190+ let widest_column = self . widest_cell_width + self . options . filling . width ( ) ;
191+ // If it exceeds terminal's width, return, since it is impossible to fit.
192+ if widest_column > self . options . width {
193+ return Dimensions {
194+ num_rows : self . cells . len ( ) ,
195+ widths : vec ! [ self . widest_cell_width] ,
196+ } ;
213197 }
214198
215- if self . cells . is_empty ( ) {
216- return Some ( Dimensions {
217- num_lines : 0 ,
218- widths : Vec :: new ( ) ,
219- } ) ;
220- }
199+ // Calculate the number of columns if all columns had the size of the largest
200+ // column. This is a lower bound on the number of columns.
201+ let min_columns = self
202+ . cells
203+ . len ( )
204+ . min ( ( self . options . width + self . options . filling . width ( ) ) / widest_column ) ;
221205
222- if self . cells . len ( ) == 1 {
223- let cell_widths = self . widths [ 0 ] ;
224- return Some ( Dimensions {
225- num_lines : 1 ,
226- widths : vec ! [ cell_widths] ,
227- } ) ;
228- }
206+ // Calculate maximum number of lines and columns.
207+ let max_rows = div_ceil ( self . cells . len ( ) , min_columns) ;
229208
230- let theoretical_max_num_lines = self . theoretical_max_num_lines ( maximum_width) ;
231- if theoretical_max_num_lines == 1 {
232- // This if—statement is necessary for the function to work correctly
233- // for small inputs.
234- return Some ( Dimensions {
235- num_lines : 1 ,
236- widths : self . widths . clone ( ) ,
237- } ) ;
238- }
239- // Instead of numbers of columns, try to find the fewest number of *lines*
240- // that the output will fit in.
241- let mut smallest_dimensions_yet = None ;
242- for num_lines in ( 1 ..=theoretical_max_num_lines) . rev ( ) {
243- // The number of columns is the number of cells divided by the number
244- // of lines, *rounded up*.
245- let num_columns = div_ceil ( self . cells . len ( ) , num_lines) ;
246-
247- // Early abort: if there are so many columns that the width of the
248- // *column separators* is bigger than the width of the screen, then
249- // don’t even try to tabulate it.
250- // This is actually a necessary check, because the width is stored as
251- // a usize, and making it go negative makes it huge instead, but it
252- // also serves as a speed-up.
253- let total_separator_width = ( num_columns - 1 ) * self . options . filling . width ( ) ;
254- if maximum_width < total_separator_width {
255- continue ;
256- }
209+ // This is a potential dimension, which can definitely fit all of the cells.
210+ let mut potential_dimension = self . compute_dimensions ( max_rows, min_columns) ;
257211
258- // Remove the separator width from the available space.
259- let adjusted_width = maximum_width - total_separator_width;
212+ // If all of the cells can be fit on one line, return immediately.
213+ if max_rows == 1 {
214+ return potential_dimension;
215+ }
260216
261- let potential_dimensions = self . compute_dimensions ( num_lines, num_columns) ;
262- if potential_dimensions. widths . iter ( ) . sum :: < usize > ( ) <= adjusted_width {
263- smallest_dimensions_yet = Some ( potential_dimensions) ;
264- } else {
217+ // Try to increase number of columns, to see if new dimension can still fit.
218+ for num_columns in min_columns + 1 ..self . cells . len ( ) {
219+ let Some ( adjusted_width) = self
220+ . options
221+ . width
222+ . checked_sub ( ( num_columns - 1 ) * self . options . filling . width ( ) )
223+ else {
265224 break ;
225+ } ;
226+ let num_rows = div_ceil ( self . cells . len ( ) , num_columns) ;
227+ let new_dimension = self . compute_dimensions ( num_rows, num_columns) ;
228+ if new_dimension. widths . iter ( ) . sum :: < usize > ( ) <= adjusted_width {
229+ potential_dimension = new_dimension;
266230 }
267231 }
268232
269- smallest_dimensions_yet
233+ potential_dimension
270234 }
271235}
272236
@@ -293,7 +257,7 @@ impl<T: AsRef<str>> fmt::Display for Grid<T> {
293257 // get exactly right.
294258 let padding = " " . repeat ( self . widest_cell_width + self . options . filling . width ( ) ) ;
295259
296- for y in 0 ..self . dimensions . num_lines {
260+ for y in 0 ..self . dimensions . num_rows {
297261 // Current position on the line.
298262 let mut cursor: usize = 0 ;
299263 for x in 0 ..self . dimensions . widths . len ( ) {
@@ -302,7 +266,7 @@ impl<T: AsRef<str>> fmt::Display for Grid<T> {
302266 let ( current, offset) = match self . options . direction {
303267 Direction :: LeftToRight => ( y * self . dimensions . widths . len ( ) + x, 1 ) ,
304268 Direction :: TopToBottom => {
305- ( y + self . dimensions . num_lines * x, self . dimensions . num_lines )
269+ ( y + self . dimensions . num_rows * x, self . dimensions . num_rows )
306270 }
307271 } ;
308272
0 commit comments