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" "math/bits"
"strconv" "strconv"
"time" "time"
"golang.org/x/text/width"
) )
func FormatBytes(c uint64) string { func FormatBytes(c uint64) string {
@ -105,3 +107,24 @@ func ToJSONString(status interface{}) string {
} }
return buf.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) 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" "strings"
"text/template" "text/template"
"github.com/restic/restic/internal/ui"
) )
// Table contains data for a table to be printed. // 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 // apply padding
pad := widths[fieldNum] - len(v) pad := widths[fieldNum] - ui.TerminalDisplayWidth(v)
if pad > 0 { if pad > 0 {
v += strings.Repeat(" ", pad) v += strings.Repeat(" ", pad)
} }
@ -139,16 +141,18 @@ func (t *Table) Write(w io.Writer) error {
columnWidths := make([]int, columns) columnWidths := make([]int, columns)
for i, desc := range t.columns { for i, desc := range t.columns {
for _, line := range strings.Split(desc, "\n") { for _, line := range strings.Split(desc, "\n") {
if columnWidths[i] < len(line) { width := ui.TerminalDisplayWidth(line)
columnWidths[i] = len(desc) if columnWidths[i] < width {
columnWidths[i] = width
} }
} }
} }
for _, line := range lines { for _, line := range lines {
for i, content := range line { for i, content := range line {
for _, l := range strings.Split(content, "\n") { for _, l := range strings.Split(content, "\n") {
if columnWidths[i] < len(l) { width := ui.TerminalDisplayWidth(l)
columnWidths[i] = len(l) if columnWidths[i] < width {
columnWidths[i] = width
} }
} }
} }
@ -159,7 +163,7 @@ func (t *Table) Write(w io.Writer) error {
for _, width := range columnWidths { for _, width := range columnWidths {
totalWidth += width totalWidth += width
} }
totalWidth += (columns - 1) * len(t.CellSeparator) totalWidth += (columns - 1) * ui.TerminalDisplayWidth(t.CellSeparator)
// write header // write header
if len(t.columns) > 0 { if len(t.columns) > 0 {

View File

@ -29,6 +29,21 @@ first column
---------------------- ----------------------
data: first data field 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 Time string
Tags, Dirs []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"}, []string{"/home/user/other"}})
table.AddRow(data{"foo", "2018-08-19 22:22:22", []string{"other", "bar"}, []string{"/home/user/other"}}) table.AddRow(data{"foo", "2018-08-19 22:22:22", []string{"other", "bar"}, []string{"/home/user/other"}})
return table return table
@ -135,7 +150,7 @@ foo 2018-08-19 22:22:22 xxx other /home/user/other
host name time zz tags dirs host name time zz tags dirs
------------------------------------------------------------ ------------------------------------------------------------
foo 2018-08-19 22:22:22 xxx work /home/user/work 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
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 bar