Browse Source

Merge pull request #7226 from gyuho/vendor

ctlv3: right-align table output, fix typo in vendor
Gyu-Ho Lee 9 years ago
parent
commit
90f6a4a28d

+ 235 - 50
cmd/vendor/github.com/olekukonko/tablewriter/table.go

@@ -9,6 +9,7 @@
 package tablewriter
 
 import (
+	"bytes"
 	"fmt"
 	"io"
 	"regexp"
@@ -20,45 +21,57 @@ const (
 )
 
 const (
-	CENTRE = "+"
-	ROW    = "-"
-	COLUMN = "|"
-	SPACE  = " "
+	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
-	lines    [][][]string
-	cs       map[int]int
-	rs       map[int]int
-	headers  []string
-	footers  []string
-	autoFmt  bool
-	autoWrap bool
-	mW       int
-	pCenter  string
-	pRow     string
-	pColumn  string
-	tColumn  int
-	tRow     int
-	align    int
-	rowLine  bool
-	hdrLine  bool
-	border   bool
-	colSize  int
+	out            io.Writer
+	rows           [][]string
+	lines          [][][]string
+	cs             map[int]int
+	rs             map[int]int
+	headers        []string
+	footers        []string
+	autoFmt        bool
+	autoWrap       bool
+	mW             int
+	pCenter        string
+	pRow           string
+	pColumn        string
+	tColumn        int
+	tRow           int
+	hAlign         int
+	fAlign         int
+	align          int
+	newLine        string
+	rowLine        bool
+	autoMergeCells bool
+	hdrLine        bool
+	borders        Border
+	colSize        int
 }
 
 // Start New Table
@@ -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()
-	t.printRows()
+	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,8 +261,45 @@ 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
@@ -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 {

+ 2 - 2
cmd/vendor/github.com/olekukonko/tablewriter/wrap.go

@@ -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

+ 3 - 0
etcdctl/ctlv3/command/printer_table.go

@@ -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()
 }

+ 1 - 1
glide.lock

@@ -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:

+ 1 - 1
glide.yaml

@@ -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: