ASCII table in golang

ASCII Table Writer

ci Total views Godoc

Generate ASCII table on the fly ... Installation is simple as

go get github.com/olekukonko/tablewriter

Features

  • Automatic Padding
  • Support Multiple Lines
  • Supports Alignment
  • Support Custom Separators
  • Automatic Alignment of numbers & percentage
  • Write directly to http , file etc via io.Writer
  • Read directly from CSV file
  • Optional row line via SetRowLine
  • Normalise table header
  • Make CSV Headers optional
  • Enable or disable table border
  • Set custom footer support
  • Optional identical cells merging
  • Set custom caption
  • Optional reflowing of paragraphs in multi-line cells.

Example 1 - Basic

data := [][]string{
    []string{"A", "The Good", "500"},
    []string{"B", "The Very very Bad Man", "288"},
    []string{"C", "The Ugly", "120"},
    []string{"D", "The Gopher", "800"},
}

table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Sign", "Rating"})

for _, v := range data {
    table.Append(v)
}
table.Render() // Send output
Output 1
+------+-----------------------+--------+
| NAME |         SIGN          | RATING |
+------+-----------------------+--------+
|  A   |       The Good        |    500 |
|  B   | The Very very Bad Man |    288 |
|  C   |       The Ugly        |    120 |
|  D   |      The Gopher       |    800 |
+------+-----------------------+--------+

Example 2 - Without Border / Footer / Bulk Append

data := [][]string{
    []string{"1/1/2014", "Domain name", "2233", "$10.98"},
    []string{"1/1/2014", "January Hosting", "2233", "$54.95"},
    []string{"1/4/2014", "February Hosting", "2233", "$51.00"},
    []string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
}

table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer
table.SetBorder(false)                                // Set Border to false
table.AppendBulk(data)                                // Add Bulk Data
table.Render()
Output 2

    DATE   |       DESCRIPTION        |  CV2  | AMOUNT
-----------+--------------------------+-------+----------
  1/1/2014 | Domain name              |  2233 | $10.98
  1/1/2014 | January Hosting          |  2233 | $54.95
  1/4/2014 | February Hosting         |  2233 | $51.00
  1/4/2014 | February Extra Bandwidth |  2233 | $30.00
-----------+--------------------------+-------+----------
                                        TOTAL | $146 93
                                      --------+----------

Example 3 - CSV

table, _ := tablewriter.NewCSV(os.Stdout, "testdata/test_info.csv", true)
table.SetAlignment(tablewriter.ALIGN_LEFT)   // Set Alignment
table.Render()
Output 3
+----------+--------------+------+-----+---------+----------------+
|  FIELD   |     TYPE     | NULL | KEY | DEFAULT |     EXTRA      |
+----------+--------------+------+-----+---------+----------------+
| user_id  | smallint(5)  | NO   | PRI | NULL    | auto_increment |
| username | varchar(10)  | NO   |     | NULL    |                |
| password | varchar(100) | NO   |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+

Example 4 - Custom Separator

table, _ := tablewriter.NewCSV(os.Stdout, "testdata/test.csv", true)
table.SetRowLine(true)         // Enable row line

// Change table lines
table.SetCenterSeparator("*")
table.SetColumnSeparator("╪")
table.SetRowSeparator("-")

table.SetAlignment(tablewriter.ALIGN_LEFT)
table.Render()
Output 4
*------------*-----------*---------*
╪ FIRST NAME ╪ LAST NAME ╪   SSN   ╪
*------------*-----------*---------*
╪ John       ╪ Barry     ╪ 123456  ╪
*------------*-----------*---------*
╪ Kathy      ╪ Smith     ╪ 687987  ╪
*------------*-----------*---------*
╪ Bob        ╪ McCornick ╪ 3979870 ╪
*------------*-----------*---------*

Example 5 - Markdown Format

data := [][]string{
	[]string{"1/1/2014", "Domain name", "2233", "$10.98"},
	[]string{"1/1/2014", "January Hosting", "2233", "$54.95"},
	[]string{"1/4/2014", "February Hosting", "2233", "$51.00"},
	[]string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
}

table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
table.SetCenterSeparator("|")
table.AppendBulk(data) // Add Bulk Data
table.Render()
Output 5
|   DATE   |       DESCRIPTION        | CV2  | AMOUNT |
|----------|--------------------------|------|--------|
| 1/1/2014 | Domain name              | 2233 | $10.98 |
| 1/1/2014 | January Hosting          | 2233 | $54.95 |
| 1/4/2014 | February Hosting         | 2233 | $51.00 |
| 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 |

Example 6 - Identical cells merging

data := [][]string{
  []string{"1/1/2014", "Domain name", "1234", "$10.98"},
  []string{"1/1/2014", "January Hosting", "2345", "$54.95"},
  []string{"1/4/2014", "February Hosting", "3456", "$51.00"},
  []string{"1/4/2014", "February Extra Bandwidth", "4567", "$30.00"},
}

table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
table.SetFooter([]string{"", "", "Total", "$146.93"})
table.SetAutoMergeCells(true)
table.SetRowLine(true)
table.AppendBulk(data)
table.Render()
Output 6
+----------+--------------------------+-------+---------+
|   DATE   |       DESCRIPTION        |  CV2  | AMOUNT  |
+----------+--------------------------+-------+---------+
| 1/1/2014 | Domain name              |  1234 | $10.98  |
+          +--------------------------+-------+---------+
|          | January Hosting          |  2345 | $54.95  |
+----------+--------------------------+-------+---------+
| 1/4/2014 | February Hosting         |  3456 | $51.00  |
+          +--------------------------+-------+---------+
|          | February Extra Bandwidth |  4567 | $30.00  |
+----------+--------------------------+-------+---------+
|                                       TOTAL | $146 93 |
+----------+--------------------------+-------+---------+

Example 7 - Identical cells merging (specify the column index to merge)

data := [][]string{
  []string{"1/1/2014", "Domain name", "1234", "$10.98"},
  []string{"1/1/2014", "January Hosting", "1234", "$10.98"},
  []string{"1/4/2014", "February Hosting", "3456", "$51.00"},
  []string{"1/4/2014", "February Extra Bandwidth", "4567", "$30.00"},
}

table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
table.SetFooter([]string{"", "", "Total", "$146.93"})
table.SetAutoMergeCellsByColumnIndex([]int{2, 3})
table.SetRowLine(true)
table.AppendBulk(data)
table.Render()
Output 7
+----------+--------------------------+-------+---------+
|   DATE   |       DESCRIPTION        |  CV2  | AMOUNT  |
+----------+--------------------------+-------+---------+
| 1/1/2014 | Domain name              |  1234 | $10.98  |
+----------+--------------------------+       +         +
| 1/1/2014 | January Hosting          |       |         |
+----------+--------------------------+-------+---------+
| 1/4/2014 | February Hosting         |  3456 | $51.00  |
+----------+--------------------------+-------+---------+
| 1/4/2014 | February Extra Bandwidth |  4567 | $30.00  |
+----------+--------------------------+-------+---------+
|                                       TOTAL | $146.93 |
+----------+--------------------------+-------+---------+

Table with color

data := [][]string{
	[]string{"1/1/2014", "Domain name", "2233", "$10.98"},
	[]string{"1/1/2014", "January Hosting", "2233", "$54.95"},
	[]string{"1/4/2014", "February Hosting", "2233", "$51.00"},
	[]string{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
}

table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
table.SetFooter([]string{"", "", "Total", "$146.93"}) // Add Footer
table.SetBorder(false)                                // Set Border to false

table.SetHeaderColor(tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor},
	tablewriter.Colors{tablewriter.FgHiRedColor, tablewriter.Bold, tablewriter.BgBlackColor},
	tablewriter.Colors{tablewriter.BgRedColor, tablewriter.FgWhiteColor},
	tablewriter.Colors{tablewriter.BgCyanColor, tablewriter.FgWhiteColor})

table.SetColumnColor(tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor},
	tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiRedColor},
	tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor},
	tablewriter.Colors{tablewriter.Bold, tablewriter.FgBlackColor})

table.SetFooterColor(tablewriter.Colors{}, tablewriter.Colors{},
	tablewriter.Colors{tablewriter.Bold},
	tablewriter.Colors{tablewriter.FgHiRedColor})

table.AppendBulk(data)
table.Render()

Table with color Output

Table with Color

Example - 8 Table Cells with Color

Individual Cell Colors from func Rich take precedence over Column Colors

data := [][]string{
	[]string{"Test1Merge", "HelloCol2 - 1", "HelloCol3 - 1", "HelloCol4 - 1"},
	[]string{"Test1Merge", "HelloCol2 - 2", "HelloCol3 - 2", "HelloCol4 - 2"},
	[]string{"Test1Merge", "HelloCol2 - 3", "HelloCol3 - 3", "HelloCol4 - 3"},
	[]string{"Test2Merge", "HelloCol2 - 4", "HelloCol3 - 4", "HelloCol4 - 4"},
	[]string{"Test2Merge", "HelloCol2 - 5", "HelloCol3 - 5", "HelloCol4 - 5"},
	[]string{"Test2Merge", "HelloCol2 - 6", "HelloCol3 - 6", "HelloCol4 - 6"},
	[]string{"Test2Merge", "HelloCol2 - 7", "HelloCol3 - 7", "HelloCol4 - 7"},
	[]string{"Test3Merge", "HelloCol2 - 8", "HelloCol3 - 8", "HelloCol4 - 8"},
	[]string{"Test3Merge", "HelloCol2 - 9", "HelloCol3 - 9", "HelloCol4 - 9"},
	[]string{"Test3Merge", "HelloCol2 - 10", "HelloCol3 -10", "HelloCol4 - 10"},
}

table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Col1", "Col2", "Col3", "Col4"})
table.SetFooter([]string{"", "", "Footer3", "Footer4"})
table.SetBorder(false)

table.SetHeaderColor(tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor},
	tablewriter.Colors{tablewriter.FgHiRedColor, tablewriter.Bold, tablewriter.BgBlackColor},
	tablewriter.Colors{tablewriter.BgRedColor, tablewriter.FgWhiteColor},
	tablewriter.Colors{tablewriter.BgCyanColor, tablewriter.FgWhiteColor})

table.SetColumnColor(tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor},
	tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiRedColor},
	tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiBlackColor},
	tablewriter.Colors{tablewriter.Bold, tablewriter.FgBlackColor})

table.SetFooterColor(tablewriter.Colors{}, tablewriter.Colors{},
	tablewriter.Colors{tablewriter.Bold},
	tablewriter.Colors{tablewriter.FgHiRedColor})

colorData1 := []string{"TestCOLOR1Merge", "HelloCol2 - COLOR1", "HelloCol3 - COLOR1", "HelloCol4 - COLOR1"}
colorData2 := []string{"TestCOLOR2Merge", "HelloCol2 - COLOR2", "HelloCol3 - COLOR2", "HelloCol4 - COLOR2"}

for i, row := range data {
	if i == 4 {
		table.Rich(colorData1, []tablewriter.Colors{tablewriter.Colors{}, tablewriter.Colors{tablewriter.Normal, tablewriter.FgCyanColor}, tablewriter.Colors{tablewriter.Bold, tablewriter.FgWhiteColor}, tablewriter.Colors{}})
		table.Rich(colorData2, []tablewriter.Colors{tablewriter.Colors{tablewriter.Normal, tablewriter.FgMagentaColor}, tablewriter.Colors{}, tablewriter.Colors{tablewriter.Bold, tablewriter.BgRedColor}, tablewriter.Colors{tablewriter.FgHiGreenColor, tablewriter.Italic, tablewriter.BgHiCyanColor}})
	}
	table.Append(row)
}

table.SetAutoMergeCells(true)
table.Render()
Table cells with color Output

Table cells with Color

Example 9 - Set table caption

data := [][]string{
    []string{"A", "The Good", "500"},
    []string{"B", "The Very very Bad Man", "288"},
    []string{"C", "The Ugly", "120"},
    []string{"D", "The Gopher", "800"},
}

table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Sign", "Rating"})
table.SetCaption(true, "Movie ratings.")

for _, v := range data {
    table.Append(v)
}
table.Render() // Send output

Note: Caption text will wrap with total width of rendered table.

Output 9
+------+-----------------------+--------+
| NAME |         SIGN          | RATING |
+------+-----------------------+--------+
|  A   |       The Good        |    500 |
|  B   | The Very very Bad Man |    288 |
|  C   |       The Ugly        |    120 |
|  D   |      The Gopher       |    800 |
+------+-----------------------+--------+
Movie ratings.

Example 10 - Set NoWhiteSpace and TablePadding option

data := [][]string{
    {"node1.example.com", "Ready", "compute", "1.11"},
    {"node2.example.com", "Ready", "compute", "1.11"},
    {"node3.example.com", "Ready", "compute", "1.11"},
    {"node4.example.com", "NotReady", "compute", "1.11"},
}

table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Status", "Role", "Version"})
table.SetAutoWrapText(false)
table.SetAutoFormatHeaders(true)
table.SetHeaderAlignment(ALIGN_LEFT)
table.SetAlignment(ALIGN_LEFT)
table.SetCenterSeparator("")
table.SetColumnSeparator("")
table.SetRowSeparator("")
table.SetHeaderLine(false)
table.SetBorder(false)
table.SetTablePadding("\t") // pad with tabs
table.SetNoWhiteSpace(true)
table.AppendBulk(data) // Add Bulk Data
table.Render()
Output 10
NAME             	STATUS  	ROLE   	VERSION 
node1.example.com	Ready   	compute	1.11   	
node2.example.com	Ready   	compute	1.11   	
node3.example.com	Ready   	compute	1.11   	
node4.example.com	NotReady	compute	1.11   	

Render table into a string

Instead of rendering the table to io.Stdout you can also render it into a string. Go 1.10 introduced the strings.Builder type which implements the io.Writer interface and can therefore be used for this task. Example:

package main

import (
    "strings"
    "fmt"

    "github.com/olekukonko/tablewriter"
)

func main() {
    tableString := &strings.Builder{}
    table := tablewriter.NewWriter(tableString)

    /*
     * Code to fill the table
     */

    table.Render()

    fmt.Println(tableString.String())
}

TODO

  • Import Directly from CSV - done
  • Support for SetFooter - done
  • Support for SetBorder - done
  • Support table with uneven rows - done
  • Support custom alignment
  • General Improvement & Optimisation
  • NewHTML Parse table from HTML
Comments
  • Add SetNoWhiteSpace and SetTablePadding options to have more kubectl like output

    Add SetNoWhiteSpace and SetTablePadding options to have more kubectl like output

    Description

    This adds the ability to output table data much more like kubectl by removing the white space at the start of rows. It also introduces a padding option so you can choose either spaces or tab as the padding between columns

    An example has been added to the README, but is posted here for visibility

    A test has also been written

    Example

    ExampleSet KubeFormat option

    data := [][]string{
        {"node1.example.com", "Ready", "compute", "1.11"},
        {"node2.example.com", "Ready", "compute", "1.11"},
        {"node3.example.com", "Ready", "compute", "1.11"},
        {"node4.example.com", "NotReady", "compute", "1.11"},
    }
    
    table := tablewriter.NewWriter(os.Stdout)
    table.SetHeader([]string{"Name", "Status", "Role", "Version"})
    table.SetAutoWrapText(false)
    table.SetAutoFormatHeaders(true)
    table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
    table.SetAlignment(tablewriter.ALIGN_LEFT)
    table.SetCenterSeparator("")
    table.SetColumnSeparator("")
    table.SetRowSeparator("")
    table.SetHeaderLine(false)
    table.SetBorder(false)
    table.SetTablePadding("\t") // pad with tabs
    table.SetNoWhiteSpace(true)
    table.AppendBulk(data) // Add Bulk Data
    table.Render()
    
    Output
    NAME             	STATUS  	ROLE   	VERSION 
    node1.example.com	Ready   	compute	1.11   	
    node2.example.com	Ready   	compute	1.11   	
    node3.example.com	Ready   	compute	1.11   	
    node4.example.com	NotReady	compute	1.11   	
    

    Closes

    This PR should close #129

  • Allow caption functionality for table

    Allow caption functionality for table

    Example - Set table caption

    data := [][]string{
        []string{"A", "The Good", "500"},
        []string{"B", "The Very very Bad Man", "288"},
        []string{"C", "The Ugly", "120"},
        []string{"D", "The Gopher", "800"},
    }
    
    table := tablewriter.NewWriter(os.Stdout)
    table.SetHeader([]string{"Name", "Sign", "Rating"})
    table.SetCaption(true, "Movie ratings.")
    
    for _, v := range data {
        table.Append(v)
    }
    table.Render() // Send output
    

    Note: Caption text will wrap with total width of rendered table.

    Output
    +------+-----------------------+--------+
    | NAME |         SIGN          | RATING |
    +------+-----------------------+--------+
    |  A   |       The Good        |    500 |
    |  B   | The Very very Bad Man |    288 |
    |  C   |       The Ugly        |    120 |
    |  D   |      The Gopher       |    800 |
    +------+-----------------------+--------+
    Movie ratings.
    
  • feature request: []string to []interface{}

    feature request: []string to []interface{}

    Append([]string) -> Append([]interface{}) This allows much more fluid table writes.

    Given:

    foo := struct{
        Foo   string
        Fizz   int
        Buzz float32
    }{
       Foo: "bar",
       Fizz: 123,
       Buzz: 3.14,
    }
    tw := tablewriter.NewWriter(os.Stdout)
    tw.SetHeader([]string{"foo", "fizz", "buzz"})
    

    this allows for

    tw.Append([]interface{}{
      foo.Foo,
      foo.Fizz,
      foo.Buzz,
    })
    

    rather than

    tw.Append([]string{}{
      foo.Foo,
      strconv.Itoa(foo.Fizz),
      strconv.FormatFloat(foo.Buzz, 'f', 2, 64),
    })
    

    This can be achieved easily by leveraging fmt.Sprint. This will cause an API break, however. All code utilizing tablewriter would require Append([]string{}) to be converted to Append([]interface{}{}).

    I'll begin a branch with this change. If you do not want to break API, then I can a new method call AppendInterface or something of that nature.

  •  Add poweron architecture ppc64le to travis build

    Add poweron architecture ppc64le to travis build

    This is part of the Ubuntu distribution for ppc64le. This helps us simplify testing later when distributions are re-building and re-releasing,For more info tag @gerrith3.

  • ANSI colours

    ANSI colours

    Using ANSI colour codes in the header strings appears to mess up, i assume, the width calculations. Colour codes in the border strings and row columns appears to be fine.

  • Add Color For Individual Cells

    Add Color For Individual Cells

    @knz @mattn @hasit

    What

    This adds a new ability SetCellColor to set the Cell Colors individually, while combined with/or without Column Colors. Cell Colors with take PRIORITY over Column Colors (otherwise it wouldn't really do anything 😸 )

    Why

    Currently if you set Column coloring the entire column will be color coded that way. There are times where it would be really nice if we can maintain the color for the column but allow for individual cells to also to also take a different coloring. Maybe we have a list of files in a table and we want to color code some of them based on certain status for example.

    Testing

    I've tested this a few different ways, i've also added a section to the readme to show how to use these column cells. The readme has the code for these generated tables. I also tested what happens if the values of (col,row) passed to SetCellColors are out of range, that isn't an issue because getColorForCell just returns an emtpy string that is checked for

    • NO COLUMN COLORS WITH CELL COLORS ( NOT MERGED )

    image

    • COLUMN COLORS WITH CELL PRIORITY ( NOT MERGED )

    image

    • NO COLUMN COLORS WITH CELL COLORS ( MERGED )

    image

    • COLUMN COLORS WITH CELL PRIORITY ( MERGED )

    image

  • Fix bug: not auto-merged if the number of lines in other columns is different

    Fix bug: not auto-merged if the number of lines in other columns is different

    Sample code:

    package main
    import (
    	"os"
    
    	"github.com/olekukonko/tablewriter"
    )
    
    func main() {
    	data := [][]string{
    		[]string{"A", "The Good", "500"},
    		[]string{"A", "The Very very very very very Bad Man", "288"},
    		[]string{"C", "The Ugly", "120"},
    		[]string{"D", "The Gopher", "800"},
    	}
    
    	table := tablewriter.NewWriter(os.Stdout)
    	table.SetHeader([]string{"Name", "Sign", "Rating"})
    	table.SetAutoMergeCells(true)
    	table.SetRowLine(true)
    
    	for _, v := range data {
    		table.Append(v)
    	}
    	table.Render()
    }
    

    Expected

    +------+--------------------------------+--------+
    | NAME |              SIGN              | RATING |
    +------+--------------------------------+--------+
    | A    | The Good                       |    500 |
    +      +--------------------------------+--------+
    |      | The Very very very very very   |    288 |
    |      | Bad Man                        |        |
    +------+--------------------------------+--------+
    | C    | The Ugly                       |    120 |
    +------+--------------------------------+--------+
    | D    | The Gopher                     |    800 |
    +------+--------------------------------+--------+
    

    Actual

    +------+--------------------------------+--------+
    | NAME |              SIGN              | RATING |
    +------+--------------------------------+--------+
    | A    | The Good                       |    500 |
    +------+--------------------------------+--------+
    | A    | The Very very very very very   |    288 |
    |      | Bad Man                        |        |
    +------+--------------------------------+--------+
    | C    | The Ugly                       |    120 |
    +------+--------------------------------+--------+
    | D    | The Gopher                     |    800 |
    +------+--------------------------------+--------+
    

    The reason for this bug is that the number of lines in the SIGN column is different. 1st line: 1 line

    The Good 
    

    2nd line: 2 lines

    The Very very very very very
    Bad Man
    

    Of course, if the number of lines is the same, it works.

    +------+----------------------------+--------+
    | NAME |            SIGN            | RATING |
    +------+----------------------------+--------+
    | A    | The Good                   |    500 |
    +      +----------------------------+--------+
    |      | The Very very very Bad Man |    288 |
    +------+----------------------------+--------+
    | C    | The Ugly                   |    120 |
    +------+----------------------------+--------+
    | D    | The Gopher                 |    800 |
    +------+----------------------------+--------+
    
  • Detect with a large number of words are in a cell, fallback to faster wrapping algorithm

    Detect with a large number of words are in a cell, fallback to faster wrapping algorithm

    We've been happily using github.com/olekukonko/tablewriter for some time to display tables of results from arbitrary database queries.

    We recently noticed that our server process was crashing due to out of memory errors even when the data returned from the database was tiny.

    We eventually tracked it down to the implementation of WrapWords() which does some N^2 memory allocation and runtime performance.

    We've written this small patch to fallback to a more performant word wrapping library call when the number of words to be wrapped exceeds an arbitrary threshold.

    This includes a test (which you should run with and without the patch to judge the impact).

    Ironically the library that it falls back to has a single issue open to Implement raggedness-aware algorithm so perhaps there's a good opportunity to collaborate there?

  • No borders, when borders are disabled

    No borders, when borders are disabled

    Even if borders are disabled we get the space borders https://github.com/olekukonko/tablewriter/blob/master/table_test.go#L268

    What would you recommend to actually disable the borders? I was preparing to make a pull request but as seen in tests this type of table behavior would be unexpected.

    My idea would be to SetBorder(false) make borders disappear and for space borders SetBorderSeparator(" ")?

  • Make uppercase header formatting optional?

    Make uppercase header formatting optional?

    Thank you for working on this library! It was really easy to set up and use.

    I'm wondering if you have any thoughts on making the headers formatting optional? I'm working on a command line tool to display the contents of our data store on appstax.com, and as the column names are case sensitive, it's a bit misleading to show the headers in all caps.

    Let me know what you think. I can take a look myself and send you a pull request if you'd like that.

  • Added `SetAutoMergeCellsByColumnIndex` used to configure columns to auto-merge. Closes #67, #150.

    Added `SetAutoMergeCellsByColumnIndex` used to configure columns to auto-merge. Closes #67, #150.

    There are a couple of issues open about SetAutoMergeCells not configurable per column basis. This PR fixes that.

    https://github.com/olekukonko/tablewriter/issues/67 https://github.com/olekukonko/tablewriter/issues/150

    SetAutoMergeCellsByColumnIndex takes an int slice (which represents the column indexes to auto-merge).

  • Add support for Unicode box drawing characters

    Add support for Unicode box drawing characters

    The ASCII lines can be hard to read under some circumstances. Using Unicode box drawing characters should on most terminals ensure that adjacent glyphs actually connect, leading to a smoother perception where lines and table content can be visually distinguished more easily.

  • Fix WrapWords excessive memory utilization

    Fix WrapWords excessive memory utilization

    When feeding production data to this library we discovered that function WordWrap had O(n*n) memory complexity, that resulted in huge amounts of memory allocated when processing even relatively small strings. We are talking Gigabytes that made our production services killed with OOM. This PR implements 2 optimisations:

    • Function splitWords was introduced to efficiently break a line into words. Note that unlike original implementation it does not consider empty spaces to be words.
    • Function WrapWords was rewritten to be O(n) on memory complexity. A benchmark that comes with this PR shows great improvement:
    BenchmarkWrapString-16
    Before:     1	2490331031 ns/op	2535184104 B/op	50905550 allocs/op
    After:   1652	    658098 ns/op	    230223 B/op	    5176 allocs/op
    
  • [bug] A long string causes the table to render extra rows

    [bug] A long string causes the table to render extra rows

    code:

    data := [][]string{
        {"1/1/2014", "Domain name", "2233", "$10.98"},
        {"1/1/2014", "January Hosting", "2233", "$54.95"},
        {"1/4/2014", "February Hosting a very very long message", "2233", "$51.00"},
        {"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
    }
    
    table := tablewriter.NewWriter(os.Stdout)
    table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
    table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
    table.SetCenterSeparator("|")
    table.SetAlignment(tablewriter.ALIGN_LEFT)
    table.AppendBulk(data)
    table.Render()
    

    result:

    | DATE | DESCRIPTION | CV2 | AMOUNT | |----------|--------------------------------|------|--------| | 1/1/2014 | Domain name | 2233 | $10.98 | | 1/1/2014 | January Hosting | 2233 | $54.95 | | 1/4/2014 | February Hosting a very very | 2233 | $51.00 | | | long message | | | | 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 |

  • fatal error: concurrent map writes

    fatal error: concurrent map writes

    fatal error: concurrent map writes

    goroutine 2208 [running]: runtime.throw({0x12adfb9, 0xc00001a8d0}) /usr/local/go/src/runtime/panic.go:1198 +0x71 fp=0xc0002cdac8 sp=0xc0002cda98 pc=0x1033cf1 runtime.mapassign_fast64(0x126a340, 0xc00007cf30, 0x22c) /usr/local/go/src/runtime/map_fast64.go:101 +0x2c5 fp=0xc0002cdb00 sp=0xc0002cdac8 pc=0x1012545 github.com/olekukonko/tablewriter.(*Table).parseDimension(0xc000168000, {0x12a9cd9, 0x0}, 0x44, 0x0) /Users/huyuan/go/pkg/mod/github.com/olekukonko/[email protected]/table.go:963 +0x29b fp=0xc0002cdbe8 sp=0xc0002cdb00 pc=0x10da25b github.com/olekukonko/tablewriter.(*Table).Append(0xc000168000, {0xc0002cdd60, 0x3, 0x0}) /Users/huyuan/go/pkg/mod/github.com/olekukonko/[email protected]/table.go:319 +0x17e fp=0xc0002cdc80 sp=0xc0002cdbe8 pc=0x10d673e loadrunner/exocr.(*ExOCR).General.func1(0xc000272000) /Users/huyuan/Desktop/代码/golang/loadrunner/exocr/exocr.go:47 +0x67c fp=0xc0002cdfc8 sp=0xc0002cdc80 pc=0x124207c loadrunner/utils.Runner·dwrap·1() /Users/huyuan/Desktop/代码/golang/loadrunner/utils/concurrent.go:18 +0x2d fp=0xc0002cdfe0 sp=0xc0002cdfc8 pc=0x12411cd runtime.goexit() /usr/local/go/src/runtime/asm_amd64.s:1581 +0x1 fp=0xc0002cdfe8 sp=0xc0002cdfe0 pc=0x1063261 created by loadrunner/utils.Runner /Users/huyuan/Desktop/代码/golang/loadrunner/utils/concurrent.go:18 +0x85

    WeChatfd26a129e4d68e6502b0cc52860791fb

    Code WeChatf232889be7be10debbb35b7971634efd : WeChat05776c727cae2ae83cdd93945892ab0c

  • incorrect placement of float64()

    incorrect placement of float64()

    https://github.com/olekukonko/tablewriter/blob/74c60be0ef6886d331ebc3c91b2ac0a7971715b6/util.go#L68

    Hello, Coverity flagged this line:

    		gapLeft := int(math.Ceil(float64(gap / 2)))
    

    as having a mistake -- the gap / 2 is truncated towards zero: https://go.dev/ref/spec#Integer_operators -- thus the math.Ceil() isn't doing anything.

    Thanks

Go package to make lightweight ASCII line graph ╭┈╯ in command line apps with no other dependencies.
Go package to make lightweight ASCII line graph ╭┈╯ in command line apps with no other dependencies.

asciigraph Go package to make lightweight ASCII line graphs ╭┈╯. Installation go get github.com/guptarohit/asciigraph Usage Basic graph package main

Jan 8, 2023
Generate ANSI-/Ascii-art version images/Gifs in your terminal.
Generate ANSI-/Ascii-art version images/Gifs in your terminal.

ANSI-Art NOTE: This toy project is not yet finished. ANSI-version Logo Block ANSI-version Logo ASCII-version Logo Support Platform You are kindly remi

Jan 6, 2023
Browser based Ascii-art generator with simple web design

Browser based Ascii-art generator with simple web design

Oct 31, 2022
OTF font with vertical bars for one-line ASCII spectrum analyzers, graphs, etc
OTF font with vertical bars for one-line ASCII spectrum analyzers, graphs, etc

graph-bars-font OTF font with vertical bars for one-line ASCII spectrum analyzers, graphs, etc. I didn't find anything similar on the net so I decided

Jul 28, 2022
CLI tool for manipulating Ceph's upmap exception table.

pgremapper When working with Ceph clusters, there are actions that cause backfill (CRUSH map changes) and cases where you want to cause backfill (movi

Aug 10, 2022
Argparse for golang. Just because `flag` sucks

Golang argparse Let's be honest -- Go's standard command line arguments parser flag terribly sucks. It cannot come anywhere close to the Python's argp

Dec 28, 2022
Golang library with POSIX-compliant command-line UI (CLI) and Hierarchical-configuration. Better substitute for stdlib flag.
Golang library with POSIX-compliant command-line UI (CLI) and Hierarchical-configuration. Better substitute for stdlib flag.

cmdr cmdr is a POSIX-compliant, command-line UI (CLI) library in Golang. It is a getopt-like parser of command-line options, be compatible with the ge

Oct 28, 2022
Fully featured Go (golang) command line option parser with built-in auto-completion support.

go-getoptions Go option parser inspired on the flexibility of Perl’s GetOpt::Long. Table of Contents Quick overview Examples Simple script Program wit

Dec 14, 2022
CONTRIBUTIONS ONLY: A Go (golang) command line and flag parser

CONTRIBUTIONS ONLY What does this mean? I do not have time to fix issues myself. The only way fixes or new features will be added is by people submitt

Dec 29, 2022
A CLI tool implemented by Golang to manage `CloudComb` resource

CloudComb CLI tool: comb Get Started comb is a CLI tool for manage resources in CloudComb base on cloudcomb-go-sdk. Support Mac, Linux and Windows. We

Jan 4, 2021
Automatically generate Go (golang) struct definitions from example JSON

gojson gojson generates go struct definitions from json or yaml documents. Example $ curl -s https://api.github.com/repos/chimeracoder/gojson | gojson

Jan 1, 2023
Command Line Alias Manager and Plugin System - Written in Golang
Command Line Alias Manager and Plugin System - Written in Golang

aly - Command Line Alias Manager and Packager Aly offers the simplest way to manage, share, and obtain command line aliases! Warning: This project is

Jun 16, 2022
💻 PTerm | Pretty Terminal Printer A golang module to print pretty text
💻 PTerm | Pretty Terminal Printer A golang module to print pretty text

✨ PTerm is a modern go module to beautify console output. Featuring charts, progressbars, tables, trees, and many more ?? It's completely configurable and 100% cross-platform compatible.

Jan 1, 2023
A collection of terminal-based widgets for richer Golang CLI apps.
A collection of terminal-based widgets for richer Golang CLI apps.

Flinch A collection of terminal-based widgets for richer Golang CLI apps. Ships with a library to build your own widgets/TUIs too. Warning: This modul

Jan 7, 2023
A really basic thread-safe progress bar for Golang applications
A really basic thread-safe progress bar for Golang applications

progressbar A very simple thread-safe progress bar which should work on every OS without problems. I needed a progressbar for croc and everything I tr

Jan 1, 2023
Console progress bar for Golang

Terminal progress bar for Go Installation go get github.com/cheggaaa/pb/v3 Documentation for v1 bar available here Quick start package main import (

Jan 9, 2023
Color package for Go (golang)
Color package for Go (golang)

color Color lets you use colorized outputs in terms of ANSI Escape Codes in Go (Golang). It has support for Windows too! The API can be used in severa

Dec 31, 2022
Golang terminal dashboard
Golang terminal dashboard

termui termui is a cross-platform and fully-customizable terminal dashboard and widget library built on top of termbox-go. It is inspired by blessed-c

Dec 29, 2022
A library for writing system daemons in golang.
A library for writing system daemons in golang.

go-daemon Library for writing system daemons in Go. Now supported only UNIX-based OS (Windows is not supported). But the library was tested only on Li

Dec 29, 2022