The table lib doesn't handle large column values well
Description:
The software currently does not handle large values properly. When dealing with large strings, the output becomes crowded, particularly due to the header line (ID Value) not aligning well with the values. The underscore or equal sign lines intended to match the length of the values are causing the output to become difficult to read and manage.
Platform info
$ go env | grep -e GOARCH -e GOHOSTOS -e GOOS -e GOVERSION
GOARCH='amd64'
GOHOSTOS='darwin'
GOOS='darwin'
GOVERSION='go1.22.2'
Observed behavior
$ go run main.go
Expected behavior The header row should not leave extra lines in the output. Maybe re-think wrap on a cell level to ensure values don't wrap auto wrap into other columns. The latter is debatable.
ID Value
__________________________________________________________________________________________________
1 long secret ...
continued long secret 1
2 long secret 2 ....
continued long secret 2
or
ID Value
__________________________________________________________________________________________________
1 long secret ...
continued long secret 1
2 long secret 2 ....
continued long secret 2
Steps to reproduce:
- create a new file named
main.go
package main
import (
"math/rand"
"github.com/fatih/color"
"github.com/rodaine/table"
)
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
func main() {
headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc()
columnFmt := color.New(color.FgYellow).SprintfFunc()
tbl := table.New("ID", "Value")
tbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt)
tbl.AddRow("1", RandStringBytes(3000))
tbl.AddRow("2", RandStringBytes(3000))
tbl.Print()
}
- Initialize go project and run the code.
$ go mod init example.com/test
$ go mod tidy
$ go run main.go
Hey @codergs! Thanks for the report. This package isn't really designed for more complex situations like that. The length of the header will always be the same width of the longest item in its column, so any padding might result in strange wrapping behavior as you see above. We aren't detecting the dimensions of the writer (since it could be anything, not just a terminal) so wrapping isn't really something that could happen automatically (and is a surprisingly harder problem than you'd think). I could see an option to specify a max column width, though. For anything more complicated, I'd look for a more robust TUI option.
@rodaine Thanks for your prompt reply. Two things come to mind, and forgive my knowledge on the inner workings here.
- How easy it is to drop the
_________below the header row? - With max width - can we support Truncation maybe?
ID Value
1 long secret 1 Lorem Ipsum is simply dummy text of the printing ....
-
Even without the header separator, the header name itself will be padded with whitespace, so you'll end up with an "empty" line (really some large amount of spaces) in there.
-
Yep, though better yet, it might be more flexible to define a custom type that decorates your values with the behavior you want (truncation or wrapping). Something like:
type Truncated[T any] struct {
Value T
MaxWidth int
}
func (t Truncated[T]) String() string {
str := fmt.Sprint(t.Value)
width := min(t.MaxWidth, len(str))
return str[:width]
}