Merge pull request #7226 from gyuho/vendor

ctlv3: right-align table output, fix typo in vendor
release-3.2
Gyu-Ho Lee 2017-01-24 12:03:58 -08:00 committed by GitHub
commit 90f6a4a28d
5 changed files with 242 additions and 54 deletions

View File

@ -9,6 +9,7 @@
package tablewriter
import (
"bytes"
"fmt"
"io"
"regexp"
@ -20,24 +21,32 @@ const (
)
const (
CENTRE = "+"
CENTER = "+"
ROW = "-"
COLUMN = "|"
SPACE = " "
NEWLINE = "\n"
)
const (
ALIGN_DEFAULT = iota
ALIGN_CENTRE
ALIGN_CENTER
ALIGN_RIGHT
ALIGN_LEFT
)
var (
decimal = regexp.MustCompile(`^\d*\.?\d*$`)
percent = regexp.MustCompile(`^\d*\.?\d*$%$`)
decimal = regexp.MustCompile(`^-*\d*\.?\d*$`)
percent = regexp.MustCompile(`^-*\d*\.?\d*$%$`)
)
type Border struct {
Left bool
Right bool
Top bool
Bottom bool
}
type Table struct {
out io.Writer
rows [][]string
@ -54,10 +63,14 @@ type Table struct {
pColumn string
tColumn int
tRow int
hAlign int
fAlign int
align int
newLine string
rowLine bool
autoMergeCells bool
hdrLine bool
border bool
borders Border
colSize int
}
@ -75,28 +88,35 @@ func NewWriter(writer io.Writer) *Table {
autoFmt: true,
autoWrap: true,
mW: MAX_ROW_WIDTH,
pCenter: CENTRE,
pCenter: CENTER,
pRow: ROW,
pColumn: COLUMN,
tColumn: -1,
tRow: -1,
hAlign: ALIGN_DEFAULT,
fAlign: ALIGN_DEFAULT,
align: ALIGN_DEFAULT,
newLine: NEWLINE,
rowLine: false,
hdrLine: true,
border: true,
borders: Border{Left: true, Right: true, Bottom: true, Top: true},
colSize: -1}
return t
}
// Render table output
func (t Table) Render() {
if t.border {
if t.borders.Top {
t.printLine(true)
}
t.printHeading()
if t.autoMergeCells {
t.printRowsMergeCells()
} else {
t.printRows()
}
if !t.rowLine && t.border {
if !t.rowLine && t.borders.Bottom {
t.printLine(true)
}
t.printFooter()
@ -151,11 +171,26 @@ func (t *Table) SetCenterSeparator(sep string) {
t.pCenter = sep
}
// Set Header Alignment
func (t *Table) SetHeaderAlignment(hAlign int) {
t.hAlign = hAlign
}
// Set Footer Alignment
func (t *Table) SetFooterAlignment(fAlign int) {
t.fAlign = fAlign
}
// Set Table Alignment
func (t *Table) SetAlignment(align int) {
t.align = align
}
// Set New Line
func (t *Table) SetNewLine(nl string) {
t.newLine = nl
}
// Set Header Line
// This would enable / disable a line after the header
func (t *Table) SetHeaderLine(line bool) {
@ -168,10 +203,20 @@ func (t *Table) SetRowLine(line bool) {
t.rowLine = line
}
// Set Auto Merge Cells
// This would enable / disable the merge of cells with identical values
func (t *Table) SetAutoMergeCells(auto bool) {
t.autoMergeCells = auto
}
// Set Table Border
// This would enable / disable line around the table
func (t *Table) SetBorder(border bool) {
t.border = border
t.SetBorders(Border{border, border, border, border})
}
func (t *Table) SetBorders(border Border) {
t.borders = border
}
// Append row to table
@ -216,10 +261,47 @@ func (t Table) printLine(nl bool) {
t.pCenter)
}
if nl {
fmt.Fprintln(t.out)
fmt.Fprint(t.out, t.newLine)
}
}
// Print line based on row width with our without cell separator
func (t Table) printLineOptionalCellSeparators(nl bool, displayCellSeparator []bool) {
fmt.Fprint(t.out, t.pCenter)
for i := 0; i < len(t.cs); i++ {
v := t.cs[i]
if i > len(displayCellSeparator) || displayCellSeparator[i] {
// Display the cell separator
fmt.Fprintf(t.out, "%s%s%s%s",
t.pRow,
strings.Repeat(string(t.pRow), v),
t.pRow,
t.pCenter)
} else {
// Don't display the cell separator for this cell
fmt.Fprintf(t.out, "%s%s",
strings.Repeat(" ", v+2),
t.pCenter)
}
}
if nl {
fmt.Fprint(t.out, t.newLine)
}
}
// Return the PadRight function if align is left, PadLeft if align is right,
// and Pad by default
func pad(align int) func(string, string, int) string {
padFunc := Pad
switch align {
case ALIGN_LEFT:
padFunc = PadRight
case ALIGN_RIGHT:
padFunc = PadLeft
}
return padFunc
}
// Print heading information
func (t Table) printHeading() {
// Check if headers is available
@ -229,11 +311,14 @@ func (t Table) printHeading() {
// Check if border is set
// Replace with space if not set
fmt.Fprint(t.out, ConditionString(t.border, t.pColumn, SPACE))
fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
// Identify last column
end := len(t.cs) - 1
// Get pad function
padFunc := pad(t.hAlign)
// Print Heading column
for i := 0; i <= end; i++ {
v := t.cs[i]
@ -241,13 +326,13 @@ func (t Table) printHeading() {
if t.autoFmt {
h = Title(h)
}
pad := ConditionString((i == end && !t.border), SPACE, t.pColumn)
pad := ConditionString((i == end && !t.borders.Left), SPACE, t.pColumn)
fmt.Fprintf(t.out, " %s %s",
Pad(h, SPACE, v),
padFunc(h, SPACE, v),
pad)
}
// Next line
fmt.Fprintln(t.out)
fmt.Fprint(t.out, t.newLine)
if t.hdrLine {
t.printLine(true)
}
@ -261,16 +346,19 @@ func (t Table) printFooter() {
}
// Only print line if border is not set
if !t.border {
if !t.borders.Bottom {
t.printLine(true)
}
// Check if border is set
// Replace with space if not set
fmt.Fprint(t.out, ConditionString(t.border, t.pColumn, SPACE))
fmt.Fprint(t.out, ConditionString(t.borders.Bottom, t.pColumn, SPACE))
// Identify last column
end := len(t.cs) - 1
// Get pad function
padFunc := pad(t.fAlign)
// Print Heading column
for i := 0; i <= end; i++ {
v := t.cs[i]
@ -278,17 +366,17 @@ func (t Table) printFooter() {
if t.autoFmt {
f = Title(f)
}
pad := ConditionString((i == end && !t.border), SPACE, t.pColumn)
pad := ConditionString((i == end && !t.borders.Top), SPACE, t.pColumn)
if len(t.footers[i]) == 0 {
pad = SPACE
}
fmt.Fprintf(t.out, " %s %s",
Pad(f, SPACE, v),
padFunc(f, SPACE, v),
pad)
}
// Next line
fmt.Fprintln(t.out)
fmt.Fprint(t.out, t.newLine)
//t.printLine(true)
hasPrinted := false
@ -304,7 +392,7 @@ func (t Table) printFooter() {
}
// Set center to be space if length is 0
if length == 0 && !t.border {
if length == 0 && !t.borders.Right {
center = SPACE
}
@ -318,7 +406,7 @@ func (t Table) printFooter() {
pad = SPACE
}
// Ignore left space of it has printed before
if hasPrinted || t.border {
if hasPrinted || t.borders.Left {
pad = t.pRow
center = t.pCenter
}
@ -339,7 +427,7 @@ func (t Table) printFooter() {
}
fmt.Fprintln(t.out)
fmt.Fprint(t.out, t.newLine)
}
@ -383,7 +471,7 @@ func (t Table) printRow(columns [][]string, colKey int) {
for y := 0; y < total; y++ {
// Check if border is set
fmt.Fprint(t.out, ConditionString((!t.border && y == 0), SPACE, t.pColumn))
fmt.Fprint(t.out, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
fmt.Fprintf(t.out, SPACE)
str := columns[y][x]
@ -391,7 +479,7 @@ func (t Table) printRow(columns [][]string, colKey int) {
// This would print alignment
// Default alignment would use multiple configuration
switch t.align {
case ALIGN_CENTRE: //
case ALIGN_CENTER: //
fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
case ALIGN_RIGHT:
fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
@ -416,14 +504,111 @@ func (t Table) printRow(columns [][]string, colKey int) {
}
// Check if border is set
// Replace with space if not set
fmt.Fprint(t.out, ConditionString(t.border, t.pColumn, SPACE))
fmt.Fprintln(t.out)
fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
fmt.Fprint(t.out, t.newLine)
}
if t.rowLine {
t.printLine(true)
}
}
// Print the rows of the table and merge the cells that are identical
func (t Table) printRowsMergeCells() {
var previousLine []string
var displayCellBorder []bool
var tmpWriter bytes.Buffer
for i, lines := range t.lines {
// We store the display of the current line in a tmp writer, as we need to know which border needs to be print above
previousLine, displayCellBorder = t.printRowMergeCells(&tmpWriter, lines, i, previousLine)
if i > 0 { //We don't need to print borders above first line
if t.rowLine {
t.printLineOptionalCellSeparators(true, displayCellBorder)
}
}
tmpWriter.WriteTo(t.out)
}
//Print the end of the table
if t.rowLine {
t.printLine(true)
}
}
// Print Row Information to a writer and merge identical cells.
// Adjust column alignment based on type
func (t Table) printRowMergeCells(writer io.Writer, columns [][]string, colKey int, previousLine []string) ([]string, []bool) {
// Get Maximum Height
max := t.rs[colKey]
total := len(columns)
// Pad Each Height
pads := []int{}
for i, line := range columns {
length := len(line)
pad := max - length
pads = append(pads, pad)
for n := 0; n < pad; n++ {
columns[i] = append(columns[i], " ")
}
}
var displayCellBorder []bool
for x := 0; x < max; x++ {
for y := 0; y < total; y++ {
// Check if border is set
fmt.Fprint(writer, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
fmt.Fprintf(writer, SPACE)
str := columns[y][x]
if t.autoMergeCells {
//Store the full line to merge mutli-lines cells
fullLine := strings.Join(columns[y], " ")
if len(previousLine) > y && fullLine == previousLine[y] && fullLine != "" {
// If this cell is identical to the one above but not empty, we don't display the border and keep the cell empty.
displayCellBorder = append(displayCellBorder, false)
str = ""
} else {
// First line or different content, keep the content and print the cell border
displayCellBorder = append(displayCellBorder, true)
}
}
// This would print alignment
// Default alignment would use multiple configuration
switch t.align {
case ALIGN_CENTER: //
fmt.Fprintf(writer, "%s", Pad(str, SPACE, t.cs[y]))
case ALIGN_RIGHT:
fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
case ALIGN_LEFT:
fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
default:
if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
} else {
fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
}
}
fmt.Fprintf(writer, SPACE)
}
// Check if border is set
// Replace with space if not set
fmt.Fprint(writer, ConditionString(t.borders.Left, t.pColumn, SPACE))
fmt.Fprint(writer, t.newLine)
}
//The new previous line is the current one
previousLine = make([]string, total)
for y := 0; y < total; y++ {
previousLine[y] = strings.Join(columns[y], " ") //Store the full line for multi-lines cells
}
//Returns the newly added line and wether or not a border should be displayed above.
return previousLine, displayCellBorder
}
func (t *Table) parseDimension(str string, colKey, rowKey int) []string {

View File

@ -23,7 +23,7 @@ const defaultPenalty = 1e5
// Wrap wraps s into a paragraph of lines of length lim, with minimal
// raggedness.
func WrapString(s string, lim int) ([]string, int) {
words := strings.Split(strings.Replace(strings.TrimSpace(s), nl, sp, -1), sp)
words := strings.Split(strings.Replace(s, nl, sp, -1), sp)
var lines []string
max := 0
for _, v := range words {
@ -96,7 +96,7 @@ func WrapWords(words []string, spc, lim, pen int) [][]string {
func getLines(s string) []string {
var lines []string
for _, line := range strings.Split(strings.TrimSpace(s), nl) {
for _, line := range strings.Split(s, nl) {
lines = append(lines, line)
}
return lines

View File

@ -31,6 +31,7 @@ func (tp *tablePrinter) MemberList(r v3.MemberListResponse) {
for _, row := range rows {
table.Append(row)
}
table.SetAlignment(tablewriter.ALIGN_RIGHT)
table.Render()
}
func (tp *tablePrinter) EndpointStatus(r []epStatus) {
@ -40,6 +41,7 @@ func (tp *tablePrinter) EndpointStatus(r []epStatus) {
for _, row := range rows {
table.Append(row)
}
table.SetAlignment(tablewriter.ALIGN_RIGHT)
table.Render()
}
func (tp *tablePrinter) DBStatus(r dbstatus) {
@ -49,5 +51,6 @@ func (tp *tablePrinter) DBStatus(r dbstatus) {
for _, row := range rows {
table.Append(row)
}
table.SetAlignment(tablewriter.ALIGN_RIGHT)
table.Render()
}

2
glide.lock generated
View File

@ -68,7 +68,7 @@ imports:
subpackages:
- pbutil
- name: github.com/olekukonko/tablewriter
version: cca8bbc0798408af109aaaa239cbd2634846b340
version: a0225b3f23b5ce0cbec6d7a66a968f8a59eca9c4
- name: github.com/prometheus/client_golang
version: c5b7fccd204277076155f10851dad72b76a49317
subpackages:

View File

@ -46,7 +46,7 @@ import:
- package: github.com/kr/pty
version: f7ee69f31298ecbe5d2b349c711e2547a617d398
- package: github.com/olekukonko/tablewriter
version: cca8bbc0798408af109aaaa239cbd2634846b340
version: a0225b3f23b5ce0cbec6d7a66a968f8a59eca9c4
- package: github.com/prometheus/client_golang
version: v0.8.0
subpackages: