summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mattn/go-runewidth/runewidth.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mattn/go-runewidth/runewidth.go')
-rw-r--r--vendor/github.com/mattn/go-runewidth/runewidth.go122
1 files changed, 69 insertions, 53 deletions
diff --git a/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/mattn/go-runewidth/runewidth.go
index 19f8e044..3d7fa560 100644
--- a/vendor/github.com/mattn/go-runewidth/runewidth.go
+++ b/vendor/github.com/mattn/go-runewidth/runewidth.go
@@ -2,6 +2,8 @@ package runewidth
import (
"os"
+
+ "github.com/rivo/uniseg"
)
//go:generate go run script/generate.go
@@ -10,11 +12,14 @@ var (
// EastAsianWidth will be set true if the current locale is CJK
EastAsianWidth bool
- // ZeroWidthJoiner is flag to set to use UTR#51 ZWJ
- ZeroWidthJoiner bool
+ // StrictEmojiNeutral should be set false if handle broken fonts
+ StrictEmojiNeutral bool = true
// DefaultCondition is a condition in current locale
- DefaultCondition = &Condition{}
+ DefaultCondition = &Condition{
+ EastAsianWidth: false,
+ StrictEmojiNeutral: true,
+ }
)
func init() {
@@ -30,7 +35,6 @@ func handleEnv() {
}
// update DefaultCondition
DefaultCondition.EastAsianWidth = EastAsianWidth
- DefaultCondition.ZeroWidthJoiner = ZeroWidthJoiner
}
type interval struct {
@@ -85,63 +89,69 @@ var nonprint = table{
// Condition have flag EastAsianWidth whether the current locale is CJK or not.
type Condition struct {
- EastAsianWidth bool
- ZeroWidthJoiner bool
+ EastAsianWidth bool
+ StrictEmojiNeutral bool
}
// NewCondition return new instance of Condition which is current locale.
func NewCondition() *Condition {
return &Condition{
- EastAsianWidth: EastAsianWidth,
- ZeroWidthJoiner: ZeroWidthJoiner,
+ EastAsianWidth: EastAsianWidth,
+ StrictEmojiNeutral: StrictEmojiNeutral,
}
}
// RuneWidth returns the number of cells in r.
// See http://www.unicode.org/reports/tr11/
func (c *Condition) RuneWidth(r rune) int {
- switch {
- case r < 0 || r > 0x10FFFF || inTables(r, nonprint, combining, notassigned):
- return 0
- case (c.EastAsianWidth && IsAmbiguousWidth(r)) || inTables(r, doublewidth):
- return 2
- default:
- return 1
- }
-}
-
-func (c *Condition) stringWidth(s string) (width int) {
- for _, r := range []rune(s) {
- width += c.RuneWidth(r)
- }
- return width
-}
-
-func (c *Condition) stringWidthZeroJoiner(s string) (width int) {
- r1, r2 := rune(0), rune(0)
- for _, r := range []rune(s) {
- if r == 0xFE0E || r == 0xFE0F {
- continue
+ // optimized version, verified by TestRuneWidthChecksums()
+ if !c.EastAsianWidth {
+ switch {
+ case r < 0x20 || r > 0x10FFFF:
+ return 0
+ case (r >= 0x7F && r <= 0x9F) || r == 0xAD: // nonprint
+ return 0
+ case r < 0x300:
+ return 1
+ case inTable(r, narrow):
+ return 1
+ case inTables(r, nonprint, combining):
+ return 0
+ case inTable(r, doublewidth):
+ return 2
+ default:
+ return 1
}
- w := c.RuneWidth(r)
- if r2 == 0x200D && inTables(r, emoji) && inTables(r1, emoji) {
- if width < w {
- width = w
- }
- } else {
- width += w
+ } else {
+ switch {
+ case r < 0 || r > 0x10FFFF || inTables(r, nonprint, combining):
+ return 0
+ case inTable(r, narrow):
+ return 1
+ case inTables(r, ambiguous, doublewidth):
+ return 2
+ case !c.StrictEmojiNeutral && inTables(r, ambiguous, emoji, narrow):
+ return 2
+ default:
+ return 1
}
- r1, r2 = r2, r
}
- return width
}
// StringWidth return width as you can see
func (c *Condition) StringWidth(s string) (width int) {
- if c.ZeroWidthJoiner {
- return c.stringWidthZeroJoiner(s)
+ g := uniseg.NewGraphemes(s)
+ for g.Next() {
+ var chWidth int
+ for _, r := range g.Runes() {
+ chWidth = c.RuneWidth(r)
+ if chWidth > 0 {
+ break // Our best guess at this point is to use the width of the first non-zero-width rune.
+ }
+ }
+ width += chWidth
}
- return c.stringWidth(s)
+ return
}
// Truncate return string truncated with w cells
@@ -149,19 +159,25 @@ func (c *Condition) Truncate(s string, w int, tail string) string {
if c.StringWidth(s) <= w {
return s
}
- r := []rune(s)
- tw := c.StringWidth(tail)
- w -= tw
- width := 0
- i := 0
- for ; i < len(r); i++ {
- cw := c.RuneWidth(r[i])
- if width+cw > w {
+ w -= c.StringWidth(tail)
+ var width int
+ pos := len(s)
+ g := uniseg.NewGraphemes(s)
+ for g.Next() {
+ var chWidth int
+ for _, r := range g.Runes() {
+ chWidth = c.RuneWidth(r)
+ if chWidth > 0 {
+ break // See StringWidth() for details.
+ }
+ }
+ if width+chWidth > w {
+ pos, _ = g.Positions()
break
}
- width += cw
+ width += chWidth
}
- return string(r[0:i]) + tail
+ return s[:pos] + tail
}
// Wrap return string wrapped with w cells
@@ -169,7 +185,7 @@ func (c *Condition) Wrap(s string, w int) string {
width := 0
out := ""
for _, r := range []rune(s) {
- cw := RuneWidth(r)
+ cw := c.RuneWidth(r)
if r == '\n' {
out += string(r)
width = 0