@@ -61,6 +61,7 @@ type Table struct {
61
61
height int
62
62
useManualHeight bool
63
63
offset int
64
+ wrap bool
64
65
65
66
// widths tracks the width of each column.
66
67
widths []int
@@ -83,6 +84,7 @@ func New() *Table {
83
84
borderLeft : true ,
84
85
borderRight : true ,
85
86
borderTop : true ,
87
+ wrap : true ,
86
88
data : NewStringData (),
87
89
}
88
90
}
@@ -217,6 +219,12 @@ func (t *Table) Offset(o int) *Table {
217
219
return t
218
220
}
219
221
222
+ // Wrap dictates whether or not the table content should wrap.
223
+ func (t * Table ) Wrap (w bool ) * Table {
224
+ t .wrap = w
225
+ return t
226
+ }
227
+
220
228
// String returns the table as a string.
221
229
func (t * Table ) String () string {
222
230
hasHeaders := len (t .headers ) > 0
@@ -234,120 +242,8 @@ func (t *Table) String() string {
234
242
}
235
243
}
236
244
237
- // Initialize the widths.
238
- t .widths = make ([]int , max (len (t .headers ), t .data .Columns ()))
239
- t .heights = make ([]int , btoi (hasHeaders )+ t .data .Rows ())
240
-
241
- // The style function may affect width of the table. It's possible to set
242
- // the StyleFunc after the headers and rows. Update the widths for a final
243
- // time.
244
- for i , cell := range t .headers {
245
- t .widths [i ] = max (t .widths [i ], lipgloss .Width (t .style (HeaderRow , i ).Render (cell )))
246
- t .heights [0 ] = max (t .heights [0 ], lipgloss .Height (t .style (HeaderRow , i ).Render (cell )))
247
- }
248
-
249
- for r := 0 ; r < t .data .Rows (); r ++ {
250
- for i := 0 ; i < t .data .Columns (); i ++ {
251
- cell := t .data .At (r , i )
252
-
253
- rendered := t .style (r , i ).Render (cell )
254
- t .heights [r + btoi (hasHeaders )] = max (t .heights [r + btoi (hasHeaders )], lipgloss .Height (rendered ))
255
- t .widths [i ] = max (t .widths [i ], lipgloss .Width (rendered ))
256
- }
257
- }
258
-
259
- // Table Resizing Logic.
260
- //
261
- // Given a user defined table width, we must ensure the table is exactly that
262
- // width. This must account for all borders, column, separators, and column
263
- // data.
264
- //
265
- // In the case where the table is narrower than the specified table width,
266
- // we simply expand the columns evenly to fit the width.
267
- // For example, a table with 3 columns takes up 50 characters total, and the
268
- // width specified is 80, we expand each column by 10 characters, adding 30
269
- // to the total width.
270
- //
271
- // In the case where the table is wider than the specified table width, we
272
- // _could_ simply shrink the columns evenly but this would result in data
273
- // being truncated (perhaps unnecessarily). The naive approach could result
274
- // in very poor cropping of the table data. So, instead of shrinking columns
275
- // evenly, we calculate the median non-whitespace length of each column, and
276
- // shrink the columns based on the largest median.
277
- //
278
- // For example,
279
- // ┌──────┬───────────────┬──────────┐
280
- // │ Name │ Age of Person │ Location │
281
- // ├──────┼───────────────┼──────────┤
282
- // │ Kini │ 40 │ New York │
283
- // │ Eli │ 30 │ London │
284
- // │ Iris │ 20 │ Paris │
285
- // └──────┴───────────────┴──────────┘
286
- //
287
- // Median non-whitespace length vs column width of each column:
288
- //
289
- // Name: 4 / 5
290
- // Age of Person: 2 / 15
291
- // Location: 6 / 10
292
- //
293
- // The biggest difference is 15 - 2, so we can shrink the 2nd column by 13.
294
-
295
- width := t .computeWidth ()
296
-
297
- if width < t .width && t .width > 0 {
298
- // Table is too narrow, expand the columns evenly until it reaches the
299
- // desired width.
300
- var i int
301
- for width < t .width {
302
- t .widths [i ]++
303
- width ++
304
- i = (i + 1 ) % len (t .widths )
305
- }
306
- } else if width > t .width && t .width > 0 {
307
- // Table is too wide, calculate the median non-whitespace length of each
308
- // column, and shrink the columns based on the largest difference.
309
- columnMedians := make ([]int , len (t .widths ))
310
- for c := range t .widths {
311
- trimmedWidth := make ([]int , t .data .Rows ())
312
- for r := 0 ; r < t .data .Rows (); r ++ {
313
- renderedCell := t .style (r + btoi (hasHeaders ), c ).Render (t .data .At (r , c ))
314
- nonWhitespaceChars := lipgloss .Width (strings .TrimRight (renderedCell , " " ))
315
- trimmedWidth [r ] = nonWhitespaceChars + 1
316
- }
317
-
318
- columnMedians [c ] = median (trimmedWidth )
319
- }
320
-
321
- // Find the biggest differences between the median and the column width.
322
- // Shrink the columns based on the largest difference.
323
- differences := make ([]int , len (t .widths ))
324
- for i := range t .widths {
325
- differences [i ] = t .widths [i ] - columnMedians [i ]
326
- }
327
-
328
- for width > t .width {
329
- index , _ := largest (differences )
330
- if differences [index ] < 1 {
331
- break
332
- }
333
-
334
- shrink := min (differences [index ], width - t .width )
335
- t .widths [index ] -= shrink
336
- width -= shrink
337
- differences [index ] = 0
338
- }
339
-
340
- // Table is still too wide, begin shrinking the columns based on the
341
- // largest column.
342
- for width > t .width {
343
- index , _ := largest (t .widths )
344
- if t .widths [index ] < 1 {
345
- break
346
- }
347
- t .widths [index ]--
348
- width --
349
- }
350
- }
245
+ // Do all the sizing calculations for width and height.
246
+ t .resize ()
351
247
352
248
var sb strings.Builder
353
249
@@ -396,15 +292,6 @@ func (t *Table) String() string {
396
292
Render (sb .String ())
397
293
}
398
294
399
- // computeWidth computes the width of the table in it's current configuration.
400
- func (t * Table ) computeWidth () int {
401
- width := sum (t .widths ) + btoi (t .borderLeft ) + btoi (t .borderRight )
402
- if t .borderColumn {
403
- width += len (t .widths ) - 1
404
- }
405
- return width
406
- }
407
-
408
295
// computeHeight computes the height of the table in it's current configuration.
409
296
func (t * Table ) computeHeight () int {
410
297
hasHeaders := len (t .headers ) > 0
@@ -556,13 +443,17 @@ func (t *Table) constructRow(index int, isOverflow bool) string {
556
443
}
557
444
558
445
cellStyle := t .style (index , c )
446
+ if ! t .wrap {
447
+ length := (cellWidth * height ) - cellStyle .GetHorizontalPadding ()
448
+ cell = ansi .Truncate (cell , length , "…" )
449
+ }
559
450
cells = append (cells , cellStyle .
560
451
// Account for the margins in the cell sizing.
561
452
Height (height - cellStyle .GetVerticalMargins ()).
562
453
MaxHeight (height ).
563
454
Width (t .widths [c ]- cellStyle .GetHorizontalMargins ()).
564
455
MaxWidth (t .widths [c ]).
565
- Render (ansi . Truncate ( cell , cellWidth * height , "…" ) ))
456
+ Render (cell ))
566
457
567
458
if c < t .data .Columns ()- 1 && t .borderColumn {
568
459
cells = append (cells , left )
0 commit comments