X Tutup
package utils import ( "fmt" "io" "sort" "strings" "github.com/cli/cli/pkg/iostreams" "github.com/cli/cli/pkg/text" ) type TablePrinter interface { IsTTY() bool AddField(string, func(int, string) string, func(string) string) EndRow() Render() error } func NewTablePrinter(io *iostreams.IOStreams) TablePrinter { if io.IsStdoutTTY() { return &ttyTablePrinter{ out: io.Out, maxWidth: io.TerminalWidth(), } } return &tsvTablePrinter{ out: io.Out, } } type tableField struct { Text string TruncateFunc func(int, string) string ColorFunc func(string) string } func (f *tableField) DisplayWidth() int { return text.DisplayWidth(f.Text) } type ttyTablePrinter struct { out io.Writer maxWidth int rows [][]tableField } func (t ttyTablePrinter) IsTTY() bool { return true } // Never pass pre-colorized text to AddField; always specify colorFunc. Otherwise, the table printer can't correctly compute the width of its columns. func (t *ttyTablePrinter) AddField(s string, truncateFunc func(int, string) string, colorFunc func(string) string) { if truncateFunc == nil { truncateFunc = text.Truncate } if t.rows == nil { t.rows = make([][]tableField, 1) } rowI := len(t.rows) - 1 field := tableField{ Text: s, TruncateFunc: truncateFunc, ColorFunc: colorFunc, } t.rows[rowI] = append(t.rows[rowI], field) } func (t *ttyTablePrinter) EndRow() { t.rows = append(t.rows, []tableField{}) } func (t *ttyTablePrinter) Render() error { if len(t.rows) == 0 { return nil } delim := " " numCols := len(t.rows[0]) colWidths := t.calculateColumnWidths(len(delim)) for _, row := range t.rows { for col, field := range row { if col > 0 { _, err := fmt.Fprint(t.out, delim) if err != nil { return err } } truncVal := field.TruncateFunc(colWidths[col], field.Text) if col < numCols-1 { // pad value with spaces on the right if padWidth := colWidths[col] - field.DisplayWidth(); padWidth > 0 { truncVal += strings.Repeat(" ", padWidth) } } if field.ColorFunc != nil { truncVal = field.ColorFunc(truncVal) } _, err := fmt.Fprint(t.out, truncVal) if err != nil { return err } } if len(row) > 0 { _, err := fmt.Fprint(t.out, "\n") if err != nil { return err } } } return nil } func (t *ttyTablePrinter) calculateColumnWidths(delimSize int) []int { numCols := len(t.rows[0]) allColWidths := make([][]int, numCols) for _, row := range t.rows { for col, field := range row { allColWidths[col] = append(allColWidths[col], field.DisplayWidth()) } } // calculate max & median content width per column maxColWidths := make([]int, numCols) // medianColWidth := make([]int, numCols) for col := 0; col < numCols; col++ { widths := allColWidths[col] sort.Ints(widths) maxColWidths[col] = widths[len(widths)-1] // medianColWidth[col] = widths[(len(widths)+1)/2] } colWidths := make([]int, numCols) // never truncate the first column colWidths[0] = maxColWidths[0] // never truncate the last column if it contains URLs if strings.HasPrefix(t.rows[0][numCols-1].Text, "https://") { colWidths[numCols-1] = maxColWidths[numCols-1] } availWidth := func() int { setWidths := 0 for col := 0; col < numCols; col++ { setWidths += colWidths[col] } return t.maxWidth - delimSize*(numCols-1) - setWidths } numFixedCols := func() int { fixedCols := 0 for col := 0; col < numCols; col++ { if colWidths[col] > 0 { fixedCols++ } } return fixedCols } // set the widths of short columns if w := availWidth(); w > 0 { if numFlexColumns := numCols - numFixedCols(); numFlexColumns > 0 { perColumn := w / numFlexColumns for col := 0; col < numCols; col++ { if max := maxColWidths[col]; max < perColumn { colWidths[col] = max } } } } firstFlexCol := -1 // truncate long columns to the remaining available width if numFlexColumns := numCols - numFixedCols(); numFlexColumns > 0 { perColumn := availWidth() / numFlexColumns for col := 0; col < numCols; col++ { if colWidths[col] == 0 { if firstFlexCol == -1 { firstFlexCol = col } if max := maxColWidths[col]; max < perColumn { colWidths[col] = max } else { colWidths[col] = perColumn } } } } // add remainder to the first flex column if w := availWidth(); w > 0 && firstFlexCol > -1 { colWidths[firstFlexCol] += w if max := maxColWidths[firstFlexCol]; max < colWidths[firstFlexCol] { colWidths[firstFlexCol] = max } } return colWidths } type tsvTablePrinter struct { out io.Writer currentCol int } func (t tsvTablePrinter) IsTTY() bool { return false } func (t *tsvTablePrinter) AddField(text string, _ func(int, string) string, _ func(string) string) { if t.currentCol > 0 { fmt.Fprint(t.out, "\t") } fmt.Fprint(t.out, text) t.currentCol++ } func (t *tsvTablePrinter) EndRow() { fmt.Fprint(t.out, "\n") t.currentCol = 0 } func (t *tsvTablePrinter) Render() error { return nil }
X Tutup