@@ -8,101 +8,157 @@ import (
88 "golang.org/x/crypto/ssh/terminal"
99)
1010
11- func NewTablePrinter (w io.Writer ) * TTYTablePrinter {
12- tty := false
13- ttyWidth := 80
11+ type TablePrinter interface {
12+ IsTTY () bool
13+ AddField (string , func (int , string ) string , func (string ) string )
14+ EndRow ()
15+ Render () error
16+ }
17+
18+ func NewTablePrinter (w io.Writer ) TablePrinter {
1419 if outFile , isFile := w .(* os.File ); isFile {
1520 fd := int (outFile .Fd ())
16- tty = terminal .IsTerminal (fd )
17- if w , _ , err := terminal .GetSize (fd ); err == nil {
18- ttyWidth = w
21+ if terminal .IsTerminal (fd ) {
22+ ttyWidth := 80
23+ if w , _ , err := terminal .GetSize (fd ); err == nil {
24+ ttyWidth = w
25+ }
26+ return & ttyTablePrinter {
27+ out : w ,
28+ maxWidth : ttyWidth ,
29+ }
1930 }
2031 }
21- return & TTYTablePrinter {
22- out : w ,
23- IsTTY : tty ,
24- maxWidth : ttyWidth ,
25- colWidths : make (map [int ]int ),
26- colFuncs : make (map [int ]func (string ) string ),
32+ return & tsvTablePrinter {
33+ out : w ,
2734 }
2835}
2936
30- type TTYTablePrinter struct {
31- out io.Writer
32- IsTTY bool
33- maxWidth int
34- colWidths map [int ]int
35- colFuncs map [int ]func (string ) string
37+ type tableField struct {
38+ Text string
39+ TruncateFunc func (int , string ) string
40+ ColorFunc func (string ) string
3641}
3742
38- func (t * TTYTablePrinter ) SetContentWidth (col , width int ) {
39- if width > t .colWidths [col ] {
40- t .colWidths [col ] = width
43+ type ttyTablePrinter struct {
44+ out io.Writer
45+ maxWidth int
46+ rows [][]tableField
47+ }
48+
49+ func (t ttyTablePrinter ) IsTTY () bool {
50+ return true
51+ }
52+
53+ func (t * ttyTablePrinter ) AddField (text string , truncateFunc func (int , string ) string , colorFunc func (string ) string ) {
54+ if truncateFunc == nil {
55+ truncateFunc = truncate
56+ }
57+ if t .rows == nil {
58+ t .rows = [][]tableField {[]tableField {}}
4159 }
60+ rowI := len (t .rows ) - 1
61+ field := tableField {
62+ Text : text ,
63+ TruncateFunc : truncateFunc ,
64+ ColorFunc : colorFunc ,
65+ }
66+ t .rows [rowI ] = append (t .rows [rowI ], field )
4267}
4368
44- func (t * TTYTablePrinter ) SetColorFunc ( col int , colorize func ( string ) string ) {
45- t .colFuncs [ col ] = colorize
69+ func (t * ttyTablePrinter ) EndRow ( ) {
70+ t .rows = append ( t . rows , [] tableField {})
4671}
4772
48- // FitColumns caps all but first column to fit available terminal width.
49- func (t * TTYTablePrinter ) FitColumns () {
50- numCols := len (t .colWidths )
51- delimWidth := 2
52- availWidth := t .maxWidth - t .colWidths [0 ] - ((numCols - 1 ) * delimWidth )
73+ func (t * ttyTablePrinter ) Render () error {
74+ if len (t .rows ) == 0 {
75+ return nil
76+ }
77+
78+ numCols := len (t .rows [0 ])
79+ colWidths := make ([]int , numCols )
80+ // measure maximum content width per column
81+ for _ , row := range t .rows {
82+ for col , field := range row {
83+ textLen := len (field .Text )
84+ if textLen > colWidths [col ] {
85+ colWidths [col ] = textLen
86+ }
87+ }
88+ }
89+
90+ delim := " "
91+ availWidth := t .maxWidth - colWidths [0 ] - ((numCols - 1 ) * len (delim ))
5392 // add extra space from columns that are already narrower than threshold
54- for col := 1 ; col < len ( t . colWidths ) ; col ++ {
93+ for col := 1 ; col < numCols ; col ++ {
5594 availColWidth := availWidth / (numCols - 1 )
56- if extra := availColWidth - t . colWidths [col ]; extra > 0 {
95+ if extra := availColWidth - colWidths [col ]; extra > 0 {
5796 availWidth += extra
5897 }
5998 }
99+ // cap all but first column to fit available terminal width
60100 // TODO: support weighted instead of even redistribution
61- for col := 1 ; col < len ( t . colWidths ) ; col ++ {
101+ for col := 1 ; col < numCols ; col ++ {
62102 availColWidth := availWidth / (numCols - 1 )
63- if t . colWidths [col ] > availColWidth {
64- t . colWidths [col ] = availColWidth
103+ if colWidths [col ] > availColWidth {
104+ colWidths [col ] = availColWidth
65105 }
66106 }
67- }
68-
69- func (t * TTYTablePrinter ) WriteRow (fields ... string ) error {
70- lastCol := len (fields ) - 1
71- delim := "\t "
72- if t .IsTTY {
73- delim = " "
74- }
75107
76- for col , val := range fields {
77- if col > 0 {
78- _ , err := fmt .Fprint (t .out , delim )
79- if err != nil {
80- return err
108+ for _ , row := range t .rows {
109+ for col , field := range row {
110+ if col > 0 {
111+ _ , err := fmt .Fprint (t .out , delim )
112+ if err != nil {
113+ return err
114+ }
81115 }
82- }
83- if t .IsTTY {
84- truncVal := truncate (t .colWidths [col ], val )
85- if col != lastCol {
86- truncVal = fmt .Sprintf ("%-*s" , t .colWidths [col ], truncVal )
116+ truncVal := field .TruncateFunc (colWidths [col ], field .Text )
117+ if col < numCols - 1 {
118+ // pad value with spaces on the right
119+ truncVal = fmt .Sprintf ("%-*s" , colWidths [col ], truncVal )
87120 }
88- if t . colFuncs [ col ] != nil {
89- truncVal = t. colFuncs [ col ] (truncVal )
121+ if field . ColorFunc != nil {
122+ truncVal = field . ColorFunc (truncVal )
90123 }
91124 _ , err := fmt .Fprint (t .out , truncVal )
92125 if err != nil {
93126 return err
94127 }
95- } else {
96- _ , err := fmt .Fprint (t .out , val )
128+ }
129+ if len (row ) > 0 {
130+ _ , err := fmt .Fprint (t .out , "\n " )
97131 if err != nil {
98132 return err
99133 }
100134 }
101135 }
102- _ , err := fmt .Fprint (t .out , "\n " )
103- if err != nil {
104- return err
136+ return nil
137+ }
138+
139+ type tsvTablePrinter struct {
140+ out io.Writer
141+ currentCol int
142+ }
143+
144+ func (t tsvTablePrinter ) IsTTY () bool {
145+ return false
146+ }
147+
148+ func (t * tsvTablePrinter ) AddField (text string , _ func (int , string ) string , _ func (string ) string ) {
149+ if t .currentCol > 0 {
150+ fmt .Fprint (t .out , "\t " )
105151 }
152+ fmt .Fprint (t .out , text )
153+ t .currentCol ++
154+ }
155+
156+ func (t * tsvTablePrinter ) EndRow () {
157+ fmt .Fprint (t .out , "\n " )
158+ t .currentCol = 0
159+ }
160+
161+ func (t * tsvTablePrinter ) Render () error {
106162 return nil
107163}
108164
0 commit comments