forked from TrueCloudLab/restic
Use character display width for table padding
Using len(...) for table cell padding produced wrong results for unicode chracters leading to misaligned tables. Implementation changed to take the actual terminal display width into consideration.
This commit is contained in:
parent
660679c2f6
commit
e9de9684f4
4 changed files with 51 additions and 8 deletions
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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},
|
||||||
|
{"ab", 3},
|
||||||
|
{"a’b", 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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,16 @@ 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) {
|
if columnWidths[i] < ui.TerminalDisplayWidth(line) {
|
||||||
columnWidths[i] = len(desc)
|
columnWidths[i] = ui.TerminalDisplayWidth(desc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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) {
|
if columnWidths[i] < ui.TerminalDisplayWidth(l) {
|
||||||
columnWidths[i] = len(l)
|
columnWidths[i] = ui.TerminalDisplayWidth(l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +161,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 {
|
||||||
|
|
|
@ -126,7 +126,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", "go’s"}, []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 +135,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
|
go’s /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
|
||||||
|
|
Loading…
Reference in a new issue