From 8dba2e0339d40a4bfad6befc6fdc2959014b99e6 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 5 Jan 2024 02:53:48 +1100 Subject: [PATCH] table: SuppressTrailingSpaces to remove all trailing spaces in output (#287) --- table/README.md | 1 + table/render_test.go | 64 ++++++++++++++++++++++++++++++++++++++++++++ table/table.go | 17 ++++++++++++ table/table_test.go | 1 + table/writer.go | 1 + 5 files changed, 84 insertions(+) diff --git a/table/README.md b/table/README.md index 406532a..6d89da4 100644 --- a/table/README.md +++ b/table/README.md @@ -21,6 +21,7 @@ Pretty-print tables into ASCII/Unicode strings. - Mirror output to an `io.Writer` (ex. `os.StdOut`) (`SetOutputMirror`) - Sort by one or more Columns (`SortBy`) - Suppress/hide columns with no content (`SuppressEmptyColumns`) + - Suppress trailing spaces in the last column (`SupressTrailingSpaces`) - Customizable Cell rendering per Column (`ColumnConfig.Transformer*`) - Hide any columns that you don't want displayed (`ColumnConfig.Hidden`) - Reset Headers/Rows/Footers at will to reuse the same Table Writer (`Reset*`) diff --git a/table/render_test.go b/table/render_test.go index abc1a72..b2400e4 100644 --- a/table/render_test.go +++ b/table/render_test.go @@ -1175,3 +1175,67 @@ func TestTable_Render_WidthEnforcer(t *testing.T) { | R123 | Small name | 2021-04-19 13:37 | Abcdefghijklmnopqrstuvwxyz | +------+----------------------+------------------+----------------------------+`) } + +func TestTable_Render_SupressTrailingSpaces(t *testing.T) { + tw := NewWriter() + tw.AppendHeader(testHeader2) + tw.AppendRows([]Row{ + {"U2", "Hey", "2021-04-19 13:37", "Yuh yuh yuh"}, + {"S12", "Uhhhh", "2021-04-19 13:37", "Some dummy data here"}, + {"R123", "Lobsters", "2021-04-19 13:37", "I like lobsters"}, + {"R123", "Some big name here and it's pretty big", "2021-04-19 13:37", "Abcdefghijklmnopqrstuvwxyz"}, + {"R123", "Small name", "2021-04-19 13:37", "Abcdefghijklmnopqrstuvwxyz"}, + }) + + t.Run("borders and separators", func(t *testing.T) { + tw.Style().Options = OptionsDefault + compareOutput(t, tw.Render(), ` ++------+----------------------------------------+------------------+----------------------------+ +| ID | TEXT1 | DATE | TEXT2 | ++------+----------------------------------------+------------------+----------------------------+ +| U2 | Hey | 2021-04-19 13:37 | Yuh yuh yuh | +| S12 | Uhhhh | 2021-04-19 13:37 | Some dummy data here | +| R123 | Lobsters | 2021-04-19 13:37 | I like lobsters | +| R123 | Some big name here and it's pretty big | 2021-04-19 13:37 | Abcdefghijklmnopqrstuvwxyz | +| R123 | Small name | 2021-04-19 13:37 | Abcdefghijklmnopqrstuvwxyz | ++------+----------------------------------------+------------------+----------------------------+`) + }) + + t.Run("no borders and separators", func(t *testing.T) { + tw.Style().Options = OptionsNoBordersAndSeparators + compareOutput(t, tw.Render(), ` + ID TEXT1 DATE TEXT2 + U2 Hey 2021-04-19 13:37 Yuh yuh yuh + S12 Uhhhh 2021-04-19 13:37 Some dummy data here + R123 Lobsters 2021-04-19 13:37 I like lobsters + R123 Some big name here and it's pretty big 2021-04-19 13:37 Abcdefghijklmnopqrstuvwxyz + R123 Small name 2021-04-19 13:37 Abcdefghijklmnopqrstuvwxyz `) + }) + + tw.SupressTrailingSpaces() + + t.Run("borders and separators suppressed spaces", func(t *testing.T) { + tw.Style().Options = OptionsDefault + compareOutput(t, tw.Render(), ` ++------+----------------------------------------+------------------+----------------------------+ +| ID | TEXT1 | DATE | TEXT2 | ++------+----------------------------------------+------------------+----------------------------+ +| U2 | Hey | 2021-04-19 13:37 | Yuh yuh yuh | +| S12 | Uhhhh | 2021-04-19 13:37 | Some dummy data here | +| R123 | Lobsters | 2021-04-19 13:37 | I like lobsters | +| R123 | Some big name here and it's pretty big | 2021-04-19 13:37 | Abcdefghijklmnopqrstuvwxyz | +| R123 | Small name | 2021-04-19 13:37 | Abcdefghijklmnopqrstuvwxyz | ++------+----------------------------------------+------------------+----------------------------+`) + }) + + t.Run("no borders and separators suppressed spaces", func(t *testing.T) { + tw.Style().Options = OptionsNoBordersAndSeparators + compareOutput(t, tw.Render(), ` +ID TEXT1 DATE TEXT2 +U2 Hey 2021-04-19 13:37 Yuh yuh yuh +S12 Uhhhh 2021-04-19 13:37 Some dummy data here +R123 Lobsters 2021-04-19 13:37 I like lobsters +R123 Some big name here and it's pretty big 2021-04-19 13:37 Abcdefghijklmnopqrstuvwxyz +R123 Small name 2021-04-19 13:37 Abcdefghijklmnopqrstuvwxyz`) + }) +} diff --git a/table/table.go b/table/table.go index 6b7c906..42e7129 100644 --- a/table/table.go +++ b/table/table.go @@ -1,6 +1,7 @@ package table import ( + "bufio" "fmt" "io" "strings" @@ -106,6 +107,8 @@ type Table struct { // suppressEmptyColumns hides columns which have no content on all regular // rows suppressEmptyColumns bool + // supressTrailingSpaces removes all trailing spaces from the end of the last column + supressTrailingSpaces bool // title contains the text to appear above the table title string } @@ -297,6 +300,12 @@ func (t *Table) SuppressEmptyColumns() { t.suppressEmptyColumns = true } +// SupressTrailingSpaces removes all trailing spaces from the end of the last column +// this is useful when OptionsNoBordersAndSeparators is used +func (t *Table) SupressTrailingSpaces() { + t.supressTrailingSpaces = true +} + func (t *Table) getAlign(colIdx int, hint renderHint) text.Align { align := text.AlignDefault if cfg, ok := t.columnConfigMap[colIdx]; ok { @@ -679,6 +688,14 @@ func (t *Table) isIndexColumn(colIdx int, hint renderHint) bool { func (t *Table) render(out *strings.Builder) string { outStr := out.String() + if t.supressTrailingSpaces { + var trimmed []string + sc := bufio.NewScanner(strings.NewReader(outStr)) + for sc.Scan() { + trimmed = append(trimmed, strings.TrimSpace(sc.Text())) + } + outStr = strings.Join(trimmed, "\n") + } if t.outputMirror != nil && len(outStr) > 0 { _, _ = t.outputMirror.Write([]byte(outStr)) _, _ = t.outputMirror.Write([]byte("\n")) diff --git a/table/table_test.go b/table/table_test.go index d4f9955..9addf88 100644 --- a/table/table_test.go +++ b/table/table_test.go @@ -19,6 +19,7 @@ var ( testFooterMultiLine = Row{"", "", "Total\nSalary", 10000} testHeader = Row{"#", "First Name", "Last Name", "Salary"} testHeaderMultiLine = Row{"#", "First\nName", "Last\nName", "Salary"} + testHeader2 = Row{"ID", "Text1", "Date", "Text2"} testRows = []Row{ {1, "Arya", "Stark", 3000}, {20, "Jon", "Snow", 2000, "You know nothing, Jon Snow!"}, diff --git a/table/writer.go b/table/writer.go index c08195b..7b57b3e 100644 --- a/table/writer.go +++ b/table/writer.go @@ -33,6 +33,7 @@ type Writer interface { SortBy(sortBy []SortBy) Style() *Style SuppressEmptyColumns() + SupressTrailingSpaces() // deprecated; in favor of Style().HTML.CSSClass SetHTMLCSSClass(cssClass string)