Merge pull request #4849 from fthoma/table-tcwidth

Use character display width for table padding
This commit is contained in:
Michael Eischer 2024-06-07 19:53:14 +00:00 committed by GitHub
commit 78485160fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 68 additions and 8 deletions

View file

@ -8,6 +8,8 @@ import (
"math/bits"
"strconv"
"time"
"golang.org/x/text/width"
)
func FormatBytes(c uint64) string {
@ -105,3 +107,24 @@ func ToJSONString(status interface{}) string {
}
return buf.String()
}
// TerminalDisplayWidth returns the number of terminal cells needed to display s
func TerminalDisplayWidth(s string) int {
width := 0
for _, r := range s {
width += terminalDisplayRuneWidth(r)
}
return width
}
func terminalDisplayRuneWidth(r rune) int {
switch width.LookupRune(r).Kind() {
case width.EastAsianWide, width.EastAsianFullwidth:
return 2
case width.EastAsianNarrow, width.EastAsianHalfwidth, width.EastAsianAmbiguous, width.Neutral:
return 1
default:
return 0
}
}

View file

@ -84,3 +84,21 @@ func TestParseBytesInvalid(t *testing.T) {
test.Equals(t, int64(0), v)
}
}
func TestTerminalDisplayWidth(t *testing.T) {
for _, c := range []struct {
input string
want int
}{
{"foo", 3},
{"aéb", 3},
{"ab", 3},
{"ab", 3},
{"aあb", 4},
} {
if got := TerminalDisplayWidth(c.input); got != c.want {
t.Errorf("wrong display width for '%s', want %d, got %d", c.input, c.want, got)
}
}
}

View file

@ -6,6 +6,8 @@ import (
"strings"
"text/template"
"github.com/restic/restic/internal/ui"
)
// Table contains data for a table to be printed.
@ -89,7 +91,7 @@ func printLine(w io.Writer, print func(io.Writer, string) error, sep string, dat
}
// apply padding
pad := widths[fieldNum] - len(v)
pad := widths[fieldNum] - ui.TerminalDisplayWidth(v)
if pad > 0 {
v += strings.Repeat(" ", pad)
}
@ -139,16 +141,18 @@ func (t *Table) Write(w io.Writer) error {
columnWidths := make([]int, columns)
for i, desc := range t.columns {
for _, line := range strings.Split(desc, "\n") {
if columnWidths[i] < len(line) {
columnWidths[i] = len(desc)
width := ui.TerminalDisplayWidth(line)
if columnWidths[i] < width {
columnWidths[i] = width
}
}
}
for _, line := range lines {
for i, content := range line {
for _, l := range strings.Split(content, "\n") {
if columnWidths[i] < len(l) {
columnWidths[i] = len(l)
width := ui.TerminalDisplayWidth(l)
if columnWidths[i] < width {
columnWidths[i] = width
}
}
}
@ -159,7 +163,7 @@ func (t *Table) Write(w io.Writer) error {
for _, width := range columnWidths {
totalWidth += width
}
totalWidth += (columns - 1) * len(t.CellSeparator)
totalWidth += (columns - 1) * ui.TerminalDisplayWidth(t.CellSeparator)
// write header
if len(t.columns) > 0 {

View file

@ -29,6 +29,21 @@ first column
----------------------
data: first data field
----------------------
`,
},
{
func(t testing.TB) *Table {
table := New()
table.AddColumn("first\ncolumn", "{{.First}}")
table.AddRow(struct{ First string }{"data"})
return table
},
`
first
column
------
data
------
`,
},
{
@ -126,7 +141,7 @@ foo 2018-08-19 22:22:22 xxx other /home/user/other
Time string
Tags, Dirs []string
}
table.AddRow(data{"foo", "2018-08-19 22:22:22", []string{"work", "go"}, []string{"/home/user/work", "/home/user/go"}})
table.AddRow(data{"foo", "2018-08-19 22:22:22", []string{"work", "gos"}, []string{"/home/user/work", "/home/user/go"}})
table.AddRow(data{"foo", "2018-08-19 22:22:22", []string{"other"}, []string{"/home/user/other"}})
table.AddRow(data{"foo", "2018-08-19 22:22:22", []string{"other", "bar"}, []string{"/home/user/other"}})
return table
@ -135,7 +150,7 @@ foo 2018-08-19 22:22:22 xxx other /home/user/other
host name time zz tags dirs
------------------------------------------------------------
foo 2018-08-19 22:22:22 xxx work /home/user/work
go /home/user/go
gos /home/user/go
foo 2018-08-19 22:22:22 xxx other /home/user/other
foo 2018-08-19 22:22:22 xxx other /home/user/other
bar