Implement wcswidth() for Go
This commit is contained in:
parent
79b1af28b4
commit
19ffbc6f3d
112
tools/tui/wcswidth.go
Normal file
112
tools/tui/wcswidth.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
func IsFlagCodepoint(ch rune) bool {
|
||||||
|
return 0x1F1E6 <= ch && ch <= 0x1F1FF
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsFlagPair(a rune, b rune) bool {
|
||||||
|
return IsFlagCodepoint(a) && IsFlagCodepoint(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
type parser_state uint8
|
||||||
|
|
||||||
|
type WCWidthIterator struct {
|
||||||
|
prev_ch rune
|
||||||
|
prev_width int
|
||||||
|
state parser_state
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WCWidthIterator) Reset() {
|
||||||
|
self.prev_ch = 0
|
||||||
|
self.prev_width = 0
|
||||||
|
self.state = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WCWidthIterator) Step(ch rune) int {
|
||||||
|
var ans int = 0
|
||||||
|
const (
|
||||||
|
normal parser_state = 0
|
||||||
|
in_esc parser_state = 1
|
||||||
|
in_csi parser_state = 2
|
||||||
|
flag_pair_started parser_state = 3
|
||||||
|
in_st_terminated parser_state = 4
|
||||||
|
)
|
||||||
|
switch self.state {
|
||||||
|
case in_csi:
|
||||||
|
self.prev_width = 0
|
||||||
|
if 0x40 <= ch && ch <= 0x7e {
|
||||||
|
self.state = normal
|
||||||
|
}
|
||||||
|
case in_st_terminated:
|
||||||
|
self.prev_width = 0
|
||||||
|
if ch == 0x9c || (ch == '\\' && self.prev_ch == 0x1b) {
|
||||||
|
self.state = normal
|
||||||
|
}
|
||||||
|
case flag_pair_started:
|
||||||
|
self.state = normal
|
||||||
|
if IsFlagPair(self.prev_ch, ch) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case normal:
|
||||||
|
switch ch {
|
||||||
|
case 0x1b:
|
||||||
|
self.prev_width = 0
|
||||||
|
self.state = in_esc
|
||||||
|
case 0xfe0f:
|
||||||
|
if IsEmojiPresentationBase(self.prev_ch) && self.prev_width == 1 {
|
||||||
|
ans += 1
|
||||||
|
self.prev_width = 2
|
||||||
|
} else {
|
||||||
|
self.prev_width = 0
|
||||||
|
}
|
||||||
|
case 0xfe0e:
|
||||||
|
if IsEmojiPresentationBase(self.prev_ch) && self.prev_width == 2 {
|
||||||
|
ans -= 1
|
||||||
|
self.prev_width = 1
|
||||||
|
} else {
|
||||||
|
self.prev_width = 0
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if IsFlagCodepoint(ch) {
|
||||||
|
self.state = flag_pair_started
|
||||||
|
}
|
||||||
|
w := Wcwidth(ch)
|
||||||
|
switch w {
|
||||||
|
case -1:
|
||||||
|
case 0:
|
||||||
|
self.prev_width = 0
|
||||||
|
case 2:
|
||||||
|
self.prev_width = 2
|
||||||
|
default:
|
||||||
|
self.prev_width = 1
|
||||||
|
}
|
||||||
|
ans += self.prev_width
|
||||||
|
}
|
||||||
|
|
||||||
|
case in_esc:
|
||||||
|
switch ch {
|
||||||
|
case '[':
|
||||||
|
self.state = in_csi
|
||||||
|
case 'P', ']', 'X', '^', '_':
|
||||||
|
self.state = in_st_terminated
|
||||||
|
case 'D', 'E', 'H', 'M', 'N', 'O', 'Z', '6', '7', '8', '9', '=', '>', 'F', 'c', 'l', 'm', 'n', 'o', '|', '}', '~':
|
||||||
|
default:
|
||||||
|
self.prev_ch = 0x1b
|
||||||
|
self.prev_width = 0
|
||||||
|
self.state = normal
|
||||||
|
return self.Step(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.prev_ch = ch
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
func Wcswidth(text string) int {
|
||||||
|
var w WCWidthIterator
|
||||||
|
ans := 0
|
||||||
|
for _, ch := range []rune(text) {
|
||||||
|
ans += w.Step(ch)
|
||||||
|
}
|
||||||
|
return ans
|
||||||
|
}
|
||||||
38
tools/tui/wcswidth_test.go
Normal file
38
tools/tui/wcswidth_test.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package tui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWCSWidth(t *testing.T) {
|
||||||
|
|
||||||
|
wcswidth := func(text string, expected int) {
|
||||||
|
if w := Wcswidth(text); w != expected {
|
||||||
|
t.Fatalf("The width for %#v was %d instead of %d", text, w, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wcwidth := func(text string, widths ...int) {
|
||||||
|
for i, q := range []rune(text) {
|
||||||
|
if w := Wcwidth(q); w != widths[i] {
|
||||||
|
t.Fatalf("The width of the char: U+%x was %d instead of %d", q, w, widths[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wcwidth("a1\000コニチ ✔", 1, 1, 0, 2, 2, 2, 1, 1)
|
||||||
|
wcswidth("a\033[2mb", 2)
|
||||||
|
wcswidth("\033a\033[2mb", 2)
|
||||||
|
wcswidth("a\033]8;id=moo;https://foo\033\\a", 2)
|
||||||
|
wcswidth("a\033x", 2)
|
||||||
|
wcswidth("\u2716\u2716\ufe0f\U0001f337", 5)
|
||||||
|
wcswidth("\u25b6\ufe0f", 2)
|
||||||
|
wcswidth("\U0001f610\ufe0e", 1)
|
||||||
|
wcswidth("\U0001f1e6a", 3)
|
||||||
|
wcswidth("\U0001F1E6a\U0001F1E8a", 6)
|
||||||
|
wcswidth("\U0001F1E6\U0001F1E8a", 3)
|
||||||
|
wcswidth("\U0001F1E6\U0001F1E8\U0001F1E6", 4)
|
||||||
|
wcswidth("a\u00adb", 2)
|
||||||
|
// Flags individually and together
|
||||||
|
wcwidth("\U0001f1ee\U0001f1f3", 2, 2)
|
||||||
|
wcswidth("\U0001f1ee\U0001f1f3", 2)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user