cli/pkg/text/truncate.go
Mislav Marohnić 1f86e7cd04 Fix display width of common punctuation characters
These characters get classified as "East Asian Mixed" by Go's
`text/width` package, and thus assumed that their printed version
occupies a width of 2 characters, whereas they each only occupy one.

I'm not sure why they are classified as East Asian, but I did not have
the energy to dive into Go's Unicode tables, so here is a workaround
based on an exclusion list.
2020-09-02 20:59:11 +02:00

80 lines
1.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package text
import (
"golang.org/x/text/width"
)
// DisplayWidth calculates what the rendered width of a string may be
func DisplayWidth(s string) int {
w := 0
for _, r := range s {
w += runeDisplayWidth(r)
}
return w
}
const (
ellipsisWidth = 3
minWidthForEllipsis = 5
)
// Truncate shortens a string to fit the maximum display width
func Truncate(max int, s string) string {
w := DisplayWidth(s)
if w <= max {
return s
}
useEllipsis := false
if max >= minWidthForEllipsis {
useEllipsis = true
max -= ellipsisWidth
}
cw := 0
ri := 0
for _, r := range s {
rw := runeDisplayWidth(r)
if cw+rw > max {
break
}
cw += rw
ri++
}
res := string([]rune(s)[:ri])
if useEllipsis {
res += "..."
}
if cw < max {
// compensate if truncating a wide character left an odd space
res += " "
}
return res
}
var runeDisplayWidthOverrides = map[rune]int{
'“': 1,
'”': 1,
'': 1,
'': 1,
'': 1, // en dash
'—': 1, // em dash
'→': 1,
'…': 1,
'•': 1, // bullet
'·': 1, // middle dot
}
func runeDisplayWidth(r rune) int {
if w, ok := runeDisplayWidthOverrides[r]; ok {
return w
}
switch width.LookupRune(r).Kind() {
case width.EastAsianWide, width.EastAsianAmbiguous, width.EastAsianFullwidth:
return 2
default:
return 1
}
}