@@ -3,6 +3,7 @@ package utils
33import (
44 "fmt"
55 "io"
6+ "sort"
67 "strings"
78
89 "github.com/cli/cli/pkg/iostreams"
@@ -34,6 +35,10 @@ type tableField struct {
3435 ColorFunc func (string ) string
3536}
3637
38+ func (f * tableField ) DisplayWidth () int {
39+ return text .DisplayWidth (f .Text )
40+ }
41+
3742type ttyTablePrinter struct {
3843 out io.Writer
3944 maxWidth int
@@ -69,35 +74,9 @@ func (t *ttyTablePrinter) Render() error {
6974 return nil
7075 }
7176
72- numCols := len (t .rows [0 ])
73- colWidths := make ([]int , numCols )
74- // measure maximum content width per column
75- for _ , row := range t .rows {
76- for col , field := range row {
77- textLen := text .DisplayWidth (field .Text )
78- if textLen > colWidths [col ] {
79- colWidths [col ] = textLen
80- }
81- }
82- }
83-
8477 delim := " "
85- availWidth := t .maxWidth - colWidths [0 ] - ((numCols - 1 ) * len (delim ))
86- // add extra space from columns that are already narrower than threshold
87- for col := 1 ; col < numCols ; col ++ {
88- availColWidth := availWidth / (numCols - 1 )
89- if extra := availColWidth - colWidths [col ]; extra > 0 {
90- availWidth += extra
91- }
92- }
93- // cap all but first column to fit available terminal width
94- // TODO: support weighted instead of even redistribution
95- for col := 1 ; col < numCols ; col ++ {
96- availColWidth := availWidth / (numCols - 1 )
97- if colWidths [col ] > availColWidth {
98- colWidths [col ] = availColWidth
99- }
100- }
78+ numCols := len (t .rows [0 ])
79+ colWidths := t .calculateColumnWidths (len (delim ))
10180
10281 for _ , row := range t .rows {
10382 for col , field := range row {
@@ -110,7 +89,7 @@ func (t *ttyTablePrinter) Render() error {
11089 truncVal := field .TruncateFunc (colWidths [col ], field .Text )
11190 if col < numCols - 1 {
11291 // pad value with spaces on the right
113- if padWidth := colWidths [col ] - text .DisplayWidth (field . Text ); padWidth > 0 {
92+ if padWidth := colWidths [col ] - field .DisplayWidth (); padWidth > 0 {
11493 truncVal += strings .Repeat (" " , padWidth )
11594 }
11695 }
@@ -132,6 +111,91 @@ func (t *ttyTablePrinter) Render() error {
132111 return nil
133112}
134113
114+ func (t * ttyTablePrinter ) calculateColumnWidths (delimSize int ) []int {
115+ numCols := len (t .rows [0 ])
116+ allColWidths := make ([][]int , numCols )
117+ for _ , row := range t .rows {
118+ for col , field := range row {
119+ allColWidths [col ] = append (allColWidths [col ], field .DisplayWidth ())
120+ }
121+ }
122+
123+ // calculate max & median content width per column
124+ maxColWidths := make ([]int , numCols )
125+ // medianColWidth := make([]int, numCols)
126+ for col := 0 ; col < numCols ; col ++ {
127+ widths := allColWidths [col ]
128+ sort .Ints (widths )
129+ maxColWidths [col ] = widths [len (widths )- 1 ]
130+ // medianColWidth[col] = widths[(len(widths)+1)/2]
131+ }
132+
133+ colWidths := make ([]int , numCols )
134+ // never truncate the first column
135+ colWidths [0 ] = maxColWidths [0 ]
136+ // never truncate the last column if it contains URLs
137+ if strings .HasPrefix (t .rows [0 ][numCols - 1 ].Text , "https://" ) {
138+ colWidths [numCols - 1 ] = maxColWidths [numCols - 1 ]
139+ }
140+
141+ availWidth := func () int {
142+ setWidths := 0
143+ for col := 0 ; col < numCols ; col ++ {
144+ setWidths += colWidths [col ]
145+ }
146+ return t .maxWidth - delimSize * (numCols - 1 ) - setWidths
147+ }
148+ numFixedCols := func () int {
149+ fixedCols := 0
150+ for col := 0 ; col < numCols ; col ++ {
151+ if colWidths [col ] > 0 {
152+ fixedCols ++
153+ }
154+ }
155+ return fixedCols
156+ }
157+
158+ // set the widths of short columns
159+ if w := availWidth (); w > 0 {
160+ if numFlexColumns := numCols - numFixedCols (); numFlexColumns > 0 {
161+ perColumn := w / numFlexColumns
162+ for col := 0 ; col < numCols ; col ++ {
163+ if max := maxColWidths [col ]; max < perColumn {
164+ colWidths [col ] = max
165+ }
166+ }
167+ }
168+ }
169+
170+ firstFlexCol := - 1
171+ // truncate long columns to the remaining available width
172+ if numFlexColumns := numCols - numFixedCols (); numFlexColumns > 0 {
173+ perColumn := availWidth () / numFlexColumns
174+ for col := 0 ; col < numCols ; col ++ {
175+ if colWidths [col ] == 0 {
176+ if firstFlexCol == - 1 {
177+ firstFlexCol = col
178+ }
179+ if max := maxColWidths [col ]; max < perColumn {
180+ colWidths [col ] = max
181+ } else {
182+ colWidths [col ] = perColumn
183+ }
184+ }
185+ }
186+ }
187+
188+ // add remainder to the first flex column
189+ if w := availWidth (); w > 0 && firstFlexCol > - 1 {
190+ colWidths [firstFlexCol ] += w
191+ if max := maxColWidths [firstFlexCol ]; max < colWidths [firstFlexCol ] {
192+ colWidths [firstFlexCol ] = max
193+ }
194+ }
195+
196+ return colWidths
197+ }
198+
135199type tsvTablePrinter struct {
136200 out io.Writer
137201 currentCol int
0 commit comments