Skip to content

Commit

Permalink
table: RowPainter to paint the entire row
Browse files Browse the repository at this point in the history
  • Loading branch information
jedib0t committed May 13, 2019
1 parent 08eca36 commit 045c5ce
Show file tree
Hide file tree
Showing 11 changed files with 166 additions and 61 deletions.
6 changes: 3 additions & 3 deletions table/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ type ColumnConfig struct {
Align text.Align
// AlignFooter defines the horizontal alignment of Footer rows
AlignFooter text.Align
// AlignFooter defines the horizontal alignment of Header rows
// AlignHeader defines the horizontal alignment of Header rows
AlignHeader text.Align

// Colors defines the colors to be used on the column
Colors text.Colors
// ColorsFooter defines the colors to be used on the column in Footer rows
ColorsFooter text.Colors
// ColorsFooter defines the colors to be used on the column in Header rows
// ColorsHeader defines the colors to be used on the column in Header rows
ColorsHeader text.Colors

// Formatter is a custom-function that changes the way the value gets
Expand All @@ -35,7 +35,7 @@ type ColumnConfig struct {
Formatter text.Formatter
// FormatterFooter is like Formatter for Footer rows
FormatterFooter text.Formatter
// FormatterFooter is like Formatter for Header rows
// FormatterHeader is like Formatter for Header rows
FormatterHeader text.Formatter

// VAlign defines the vertical alignment
Expand Down
2 changes: 1 addition & 1 deletion table/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (t *Table) Render() string {
t.renderRowsHeader(&out)

// (data) rows
t.renderRows(&out, t.getRowsSorted(), renderHint{})
t.renderRows(&out, t.rows, renderHint{})

// footer rows
t.renderRowsFooter(&out)
Expand Down
2 changes: 1 addition & 1 deletion table/render_csv.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (t *Table) RenderCSV() string {
var out strings.Builder
if t.numColumns > 0 {
t.csvRenderRows(&out, t.rowsHeader)
t.csvRenderRows(&out, t.getRowsSorted())
t.csvRenderRows(&out, t.rows)
t.csvRenderRows(&out, t.rowsFooter)
}
return t.render(&out)
Expand Down
2 changes: 1 addition & 1 deletion table/render_html.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (t *Table) RenderHTML() string {
}
out.WriteString("\">\n")
t.htmlRenderRows(&out, t.rowsHeader, renderHint{isHeaderRow: true})
t.htmlRenderRows(&out, t.getRowsSorted(), renderHint{})
t.htmlRenderRows(&out, t.rows, renderHint{})
t.htmlRenderRows(&out, t.rowsFooter, renderHint{isFooterRow: true})
out.WriteString("</table>")
}
Expand Down
2 changes: 1 addition & 1 deletion table/render_markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func (t *Table) RenderMarkdown() string {
var out strings.Builder
if t.numColumns > 0 {
t.markdownRenderRows(&out, t.rowsHeader, true, false)
t.markdownRenderRows(&out, t.getRowsSorted(), false, false)
t.markdownRenderRows(&out, t.rows, false, false)
t.markdownRenderRows(&out, t.rowsFooter, false, true)
if t.caption != "" {
out.WriteString("\n_")
Expand Down
52 changes: 52 additions & 0 deletions table/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,58 @@ func TestTable_Render_Paged(t *testing.T) {
assert.Equal(t, expectedOut, tw.Render())
}

func TestTable_Render_RowPainter(t *testing.T) {
tw := NewWriter()
tw.AppendHeader(testHeader)
tw.AppendRows(testRows)
tw.AppendRow(testRowMultiLine)
tw.AppendFooter(testFooter)
tw.SetIndexColumn(1)
tw.SetRowPainter(RowPainter(func(row Row) text.Colors {
salary := row[3].(int)
if salary > 3000 {
return text.Colors{text.BgYellow, text.FgBlack}
} else if salary < 2000 {
return text.Colors{text.BgRed, text.FgBlack}
}
return nil
}))
tw.SetStyle(StyleLight)
tw.SortBy([]SortBy{{Name: "Salary", Mode: AscNumeric}})

expectedOutLines := []string{
"┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐",
"│ # │ FIRST NAME │ LAST NAME │ SALARY │ │",
"├─────┼────────────┼───────────┼────────┼─────────────────────────────┤",
"│ 0 │\x1b[41;30m Winter \x1b[0m│\x1b[41;30m Is \x1b[0m│\x1b[41;30m 0 \x1b[0m│\x1b[41;30m Coming. \x1b[0m│",
"│ │\x1b[41;30m \x1b[0m│\x1b[41;30m \x1b[0m│\x1b[41;30m \x1b[0m│\x1b[41;30m The North Remembers! \x1b[0m│",
"│ │\x1b[41;30m \x1b[0m│\x1b[41;30m \x1b[0m│\x1b[41;30m \x1b[0m│\x1b[41;30m This is known. \x1b[0m│",
"│ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │",
"│ 1 │ Arya │ Stark │ 3000 │ │",
"│ 300 │\x1b[43;30m Tyrion \x1b[0m│\x1b[43;30m Lannister \x1b[0m│\x1b[43;30m 5000 \x1b[0m│\x1b[43;30m \x1b[0m│",
"├─────┼────────────┼───────────┼────────┼─────────────────────────────┤",
"│ │ │ TOTAL │ 10000 │ │",
"└─────┴────────────┴───────────┴────────┴─────────────────────────────┘",
}
expectedOut := strings.Join(expectedOutLines, "\n")
assert.Equal(t, expectedOut, tw.Render())

tw.SetStyle(StyleColoredBright)
tw.Style().Color.RowAlternate = tw.Style().Color.Row
expectedOutLines = []string{
"\x1b[106;30m # \x1b[0m\x1b[106;30m FIRST NAME \x1b[0m\x1b[106;30m LAST NAME \x1b[0m\x1b[106;30m SALARY \x1b[0m\x1b[106;30m \x1b[0m",
"\x1b[106;30m 0 \x1b[0m\x1b[41;30m Winter \x1b[0m\x1b[41;30m Is \x1b[0m\x1b[41;30m 0 \x1b[0m\x1b[41;30m Coming. \x1b[0m",
"\x1b[106;30m \x1b[0m\x1b[41;30m \x1b[0m\x1b[41;30m \x1b[0m\x1b[41;30m \x1b[0m\x1b[41;30m The North Remembers! \x1b[0m",
"\x1b[106;30m \x1b[0m\x1b[41;30m \x1b[0m\x1b[41;30m \x1b[0m\x1b[41;30m \x1b[0m\x1b[41;30m This is known. \x1b[0m",
"\x1b[106;30m 20 \x1b[0m\x1b[107;30m Jon \x1b[0m\x1b[107;30m Snow \x1b[0m\x1b[107;30m 2000 \x1b[0m\x1b[107;30m You know nothing, Jon Snow! \x1b[0m",
"\x1b[106;30m 1 \x1b[0m\x1b[107;30m Arya \x1b[0m\x1b[107;30m Stark \x1b[0m\x1b[107;30m 3000 \x1b[0m\x1b[107;30m \x1b[0m",
"\x1b[106;30m 300 \x1b[0m\x1b[43;30m Tyrion \x1b[0m\x1b[43;30m Lannister \x1b[0m\x1b[43;30m 5000 \x1b[0m\x1b[43;30m \x1b[0m",
"\x1b[46;30m \x1b[0m\x1b[46;30m \x1b[0m\x1b[46;30m TOTAL \x1b[0m\x1b[46;30m 10000 \x1b[0m\x1b[46;30m \x1b[0m",
}
expectedOut = strings.Join(expectedOutLines, "\n")
assert.Equal(t, expectedOut, tw.Render())
}

func TestTable_Render_Sorted(t *testing.T) {
tw := NewWriter()
tw.AppendHeader(testHeader)
Expand Down
86 changes: 62 additions & 24 deletions table/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import (
// Row defines a single row in the Table.
type Row []interface{}

// RowPainter is a custom function that takes a Row as input and returns the
// text.Colors{} to use on the entire row
type RowPainter func(row Row) text.Colors

// rowStr defines a single row in the Table comprised of just string objects.
type rowStr []string

Expand Down Expand Up @@ -68,6 +72,9 @@ type Table struct {
pageSize int
// rows stores the rows that make up the body (in string form)
rows []rowStr
// rowsColors stores the text.Colors over-rides for each row as defined by
// rowPainter
rowsColors []text.Colors
// rowsRaw stores the rows that make up the body
rowsRaw []Row
// rowsFooter stores the rows that make up the footer (in string form)
Expand All @@ -78,6 +85,9 @@ type Table struct {
rowsHeader []rowStr
// rowsHeaderRaw stores the rows that make up the header
rowsHeaderRaw []Row
// rowPainter is a custom function that given a Row, returns the colors to
// use on the entire row
rowPainter RowPainter
// rowSeparator is a dummy row that contains the separator columns (dashes
// that make up the separator between header/body/footer
rowSeparator rowStr
Expand Down Expand Up @@ -223,6 +233,11 @@ func (t *Table) SetPageSize(numLines int) {
t.pageSize = numLines
}

// SetRowPainter sets the RowPainter override.
func (t *Table) SetRowPainter(painter RowPainter) {
t.rowPainter = painter
}

// SetStyle overrides the DefaultStyle with the provided one.
func (t *Table) SetStyle(style Style) {
t.style = &style
Expand Down Expand Up @@ -316,7 +331,8 @@ func (t *Table) getAlign(colIdx int, hint renderHint) text.Align {
} else {
align = cfg.Align
}
} else {
}
if align == text.AlignDefault {
align = t.getAlignOld(colIdx, hint)
}
if align == text.AlignDefault && !t.columnIsNonNumeric[colIdx] {
Expand Down Expand Up @@ -349,7 +365,22 @@ func (t *Table) getAutoIndexColumnIDs() rowStr {
return row
}

func (t *Table) getBorderColors(hint renderHint) text.Colors {
if hint.isFooterRow {
return t.style.Color.Footer
} else if t.autoIndex {
return t.style.Color.IndexColumn
}
return t.style.Color.Header
}

func (t *Table) getColumnColors(colIdx int, hint renderHint) text.Colors {
if t.rowPainter != nil && hint.isRegularRow() && !t.isIndexColumn(colIdx, hint) {
colors := t.rowsColors[hint.rowNumber-1]
if colors != nil {
return colors
}
}
if cfg, ok := t.columnConfigMap[colIdx]; ok {
if hint.isSeparatorRow {
return nil
Expand Down Expand Up @@ -420,28 +451,6 @@ func (t *Table) getRowColors(hint renderHint) []text.Colors {
return t.colors
}

func (t *Table) getRowsSorted() []rowStr {
if t.sortBy == nil || len(t.sortBy) == 0 {
return t.rows
}

sortedRowIndices := t.getSortedRowIndices()
sortedRows := make([]rowStr, len(t.rows))
for idx := range t.rows {
sortedRows[idx] = t.rows[sortedRowIndices[idx]]
}
return sortedRows
}

func (t *Table) getBorderColors(hint renderHint) text.Colors {
if hint.isFooterRow {
return t.style.Color.Footer
} else if t.autoIndex {
return t.style.Color.IndexColumn
}
return t.style.Color.Header
}

func (t *Table) getSeparatorColors(hint renderHint) text.Colors {
if hint.isHeaderRow {
return t.style.Color.Header
Expand All @@ -465,7 +474,8 @@ func (t *Table) getVAlign(colIdx int, hint renderHint) text.VAlign {
} else {
vAlign = cfg.VAlign
}
} else {
}
if vAlign == text.VAlignDefault {
vAlign = t.getVAlignOld(colIdx, hint)
}
return vAlign
Expand Down Expand Up @@ -570,14 +580,38 @@ func (t *Table) initForRenderColumnLengths() {
}

func (t *Table) initForRenderRows() {
t.rowsColors = nil
if t.rowPainter != nil {
t.rowsColors = make([]text.Colors, len(t.rowsRaw))
}
t.rows = t.initForRenderRowsStringify(t.rowsRaw, renderHint{})
t.rowsFooter = t.initForRenderRowsStringify(t.rowsFooterRaw, renderHint{isFooterRow: true})
t.rowsHeader = t.initForRenderRowsStringify(t.rowsHeaderRaw, renderHint{isHeaderRow: true})

// sort rows and rowsColors
if len(t.sortBy) > 0 {
sortedRowIndices := t.getSortedRowIndices()
sortedRows := make([]rowStr, len(t.rows))
for idx := range t.rows {
sortedRows[idx] = t.rows[sortedRowIndices[idx]]
}
t.rows = sortedRows
if t.rowsColors != nil {
sortedRowsColors := make([]text.Colors, len(t.rows))
for idx := range t.rows {
sortedRowsColors[idx] = t.rowsColors[sortedRowIndices[idx]]
}
t.rowsColors = sortedRowsColors
}
}
}

func (t *Table) initForRenderRowsStringify(rows []Row, hint renderHint) []rowStr {
rowsStr := make([]rowStr, len(rows))
for idx, row := range rows {
if t.rowPainter != nil && hint.isRegularRow() {
t.rowsColors[idx] = t.rowPainter(row)
}
rowsStr[idx] = t.analyzeAndStringify(row, hint)
}
return rowsStr
Expand All @@ -595,6 +629,10 @@ func (t *Table) initForRenderRowSeparator() {
}
}

func (t *Table) isIndexColumn(colIdx int, hint renderHint) bool {
return t.indexColumn == colIdx+1 || hint.isAutoIndexColumn
}

func (t *Table) render(out *strings.Builder) string {
outStr := out.String()
if t.outputMirror != nil && len(outStr) > 0 {
Expand Down
2 changes: 1 addition & 1 deletion table/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ func TestTable_SetColorsHeader(t *testing.T) {
assert.Equal(t, 2, len(table.colorsHeader))
}

func TestTable_SetColumnConfig(t *testing.T) {
func TestTable_SetColumnConfigs(t *testing.T) {
table := Table{}
assert.Empty(t, table.columnConfigs)

Expand Down
43 changes: 23 additions & 20 deletions table/writer.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package table

import (
"github.com/jedib0t/go-pretty/text"
"io"

"github.com/jedib0t/go-pretty/text"
)

// Writer declares the interfaces that can be used to setup and render a table.
Expand All @@ -16,37 +17,39 @@ type Writer interface {
RenderCSV() string
RenderHTML() string
RenderMarkdown() string
// deprecated
SetAlign(align []text.Align)
// deprecated
SetAlignFooter(align []text.Align)
// deprecated
SetAlignHeader(align []text.Align)
// deprecated
SetAllowedColumnLengths(lengths []int)
SetAllowedRowLength(length int)
SetAutoIndex(autoIndex bool)
SetCaption(format string, a ...interface{})
// deprecated
SetColors(colors []text.Colors)
// deprecated
SetColorsFooter(colors []text.Colors)
// deprecated
SetColorsHeader(colors []text.Colors)
SetColumnConfigs(configs []ColumnConfig)
SetHTMLCSSClass(cssClass string)
SetIndexColumn(colNum int)
SetOutputMirror(mirror io.Writer)
SetPageSize(numLines int)
SetRowPainter(painter RowPainter)
SetStyle(style Style)
// deprecated
SortBy(sortBy []SortBy)
Style() *Style

// deprecated; use SetColumnOptions instead
SetAlign(align []text.Align)
// deprecated; use SetColumnOptions instead
SetAlignFooter(align []text.Align)
// deprecated; use SetColumnOptions instead
SetAlignHeader(align []text.Align)
// deprecated; use SetColumnOptions instead
SetAllowedColumnLengths(lengths []int)
// deprecated; use SetColumnOptions instead
SetColors(colors []text.Colors)
// deprecated; use SetColumnOptions instead
SetColorsFooter(colors []text.Colors)
// deprecated; use SetColumnOptions instead
SetColorsHeader(colors []text.Colors)
// deprecated; use SetColumnOptions instead
SetVAlign(vAlign []text.VAlign)
// deprecated
// deprecated; use SetColumnOptions instead
SetVAlignFooter(vAlign []text.VAlign)
// deprecated
// deprecated; use SetColumnOptions instead
SetVAlignHeader(vAlign []text.VAlign)
SortBy(sortBy []SortBy)
Style() *Style
}

// NewWriter initializes and returns a Writer.
Expand Down

0 comments on commit 045c5ce

Please sign in to comment.