1
0
mirror of https://github.com/mum4k/termdash.git synced 2025-04-25 13:48:50 +08:00

A wrapper over the external runewidth package.

This commit is contained in:
Jakub Sobon 2019-02-21 23:24:30 -05:00
parent c9e65178e6
commit 43e15c302e
No known key found for this signature in database
GPG Key ID: F2451A77FB05D3B7
2 changed files with 127 additions and 0 deletions

View File

@ -0,0 +1,30 @@
// runewidth is a wrapper over github.com/mattn/go-runewidth which gives
// different treatment to certain runes with ambiguous width.
package runewidth
import runewidth "github.com/mattn/go-runewidth"
// RuneWidth returns the number of cells needed to draw r.
// Background in http://www.unicode.org/reports/tr11/.
//
// Treats runes used internally by termdash as single-cell (half-width) runes
// regardless of the locale. I.e. runes that are used to draw lines, boxes,
// indicate resize or text trimming was needed and runes used by the braille
// canvas.
//
// This should be safe, since even in locales where these runes have ambiguous
// width, we still place all the character content around them so they should
// have be half-width.
func RuneWidth(r rune) int {
return runewidth.RuneWidth(r)
}
// StringWidth is like RuneWidth, but returns the number of cells occupied by
// all the runes in the string.
func StringWidth(s string) int {
var width int
for _, r := range []rune(s) {
width += RuneWidth(r)
}
return width
}

View File

@ -0,0 +1,97 @@
package runewidth
import (
"testing"
runewidth "github.com/mattn/go-runewidth"
)
func TestRuneWidth(t *testing.T) {
tests := []struct {
desc string
runes []rune
eastAsian bool
want int
}{
{
desc: "ascii characters",
runes: []rune{'a', 'f', '#'},
want: 1,
},
{
desc: "non-printable characters from mattn/runewidth/runewidth_test",
runes: []rune{'\x00', '\x01', '\u0300', '\u2028', '\u2029'},
want: 0,
},
{
desc: "half-width runes from mattn/runewidth/runewidth_test",
runes: []rune{'セ', 'カ', 'イ', '☆'},
want: 1,
},
{
desc: "full-width runes from mattn/runewidth/runewidth_test",
runes: []rune{'世', '界'},
want: 2,
},
{
desc: "ambiguous so double-width in eastAsian from mattn/runewidth/runewidth_test",
runes: []rune{'☆'},
eastAsian: true,
want: 2,
},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
runewidth.DefaultCondition.EastAsianWidth = tc.eastAsian
defer func() {
runewidth.DefaultCondition.EastAsianWidth = false
}()
for _, r := range tc.runes {
if got := RuneWidth(r); got != tc.want {
t.Errorf("RuneWidth(%v) => %v, want %v", r, got, tc.want)
}
}
})
}
}
func TestStringWidth(t *testing.T) {
tests := []struct {
desc string
str string
eastAsian bool
want int
}{
{
desc: "ascii characters",
str: "hello",
want: 5,
},
{
desc: "string from mattn/runewidth/runewidth_test",
str: "■㈱の世界①",
want: 10,
},
{
desc: "string in eastAsian from mattn/runewidth/runewidth_test",
str: "■㈱の世界①",
eastAsian: true,
want: 12,
},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
runewidth.DefaultCondition.EastAsianWidth = tc.eastAsian
defer func() {
runewidth.DefaultCondition.EastAsianWidth = false
}()
if got := StringWidth(tc.str); got != tc.want {
t.Errorf("StringWidth(%q) => %v, want %v", tc.str, got, tc.want)
}
})
}
}