Port wcswidth to use grapheme segmentation

This commit is contained in:
Kovid Goyal
2025-04-04 11:09:35 +05:30
parent 2cad589f1c
commit 203e9f6c58
4 changed files with 73 additions and 100 deletions

View File

@@ -12,21 +12,12 @@ import (
var _ = fmt.Print
func IsFlagCodepoint(ch rune) bool {
return 0x1F1E6 <= ch && ch <= 0x1F1FF
}
func IsFlagPair(a rune, b rune) bool {
return IsFlagCodepoint(a) && IsFlagCodepoint(b)
}
type ecparser_state uint8
type WCWidthIterator struct {
prev_ch rune
prev_width, current_width int
seg GraphemeSegmentationResult
can_combine bool
parser EscapeCodeParser
state ecparser_state
rune_count uint
}
@@ -34,6 +25,12 @@ func CreateWCWidthIterator() *WCWidthIterator {
var ans WCWidthIterator
ans.parser.HandleRune = ans.handle_rune
ans.parser.HandleCSI = ans.handle_csi
ans.parser.HandleOSC = ans.handle_st_terminated
ans.parser.HandleDCS = ans.handle_st_terminated
ans.parser.HandlePM = ans.handle_st_terminated
ans.parser.HandleSOS = ans.handle_st_terminated
ans.parser.HandleAPC = ans.handle_st_terminated
return &ans
}
@@ -42,6 +39,8 @@ func (self *WCWidthIterator) Reset() {
self.prev_width = 0
self.current_width = 0
self.rune_count = 0
self.can_combine = false
self.seg = 0
self.parser.Reset()
}
@@ -58,54 +57,47 @@ func (self *WCWidthIterator) handle_csi(csi []byte) error {
}
}
}
self.can_combine = false
self.seg = 0
return nil
}
func (self *WCWidthIterator) handle_st_terminated(data []byte) error {
self.can_combine = false
self.seg = 0
return nil
}
func (self *WCWidthIterator) handle_rune(ch rune) error {
self.rune_count += 1
const (
normal ecparser_state = 0
flag_pair_started ecparser_state = 3
)
switch self.state {
case flag_pair_started:
self.state = normal
if IsFlagPair(self.prev_ch, ch) {
break
}
fallthrough
case normal:
cp := CharPropsFor(ch)
self.seg = self.seg.Step(cp)
if self.can_combine && self.seg.Add_to_current_cell() == 1 {
switch ch {
case 0xfe0f:
if IsEmojiPresentationBase(self.prev_ch) && self.prev_width == 1 {
if CharPropsFor(self.prev_ch).Is_emoji_presentation_base() == 1 && self.prev_width == 1 {
self.current_width += 1
self.prev_width = 2
} else {
self.prev_width = 0
}
case 0xfe0e:
if IsEmojiPresentationBase(self.prev_ch) && self.prev_width == 2 {
if CharPropsFor(self.prev_ch).Is_emoji_presentation_base() == 1 && self.prev_width == 2 {
self.current_width -= 1
self.prev_width = 1
} else {
self.prev_width = 0
}
default:
if IsFlagCodepoint(ch) {
self.state = flag_pair_started
}
w := Runewidth(ch)
switch w {
case -1:
case 0:
self.prev_width = 0
case 2:
self.prev_width = 2
default:
self.prev_width = 1
}
self.current_width += self.prev_width
}
} else {
width := cp.Width()
switch width {
case -1:
case 0:
self.prev_width = 0
case 2:
self.prev_width = 2
default:
self.prev_width = 1
}
self.current_width += self.prev_width
self.can_combine = true
}
self.prev_ch = ch
return nil