1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
package wcswidth
import (
"fmt"
"strconv"
"strings"
"github.com/kovidgoyal/kitty/tools/utils"
)
var _ = fmt.Print
type WCWidthIterator struct {
prev_ch rune
prev_width, current_width int
seg GraphemeSegmentationResult
can_combine bool
parser EscapeCodeParser
rune_count uint
}
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
}
func (self *WCWidthIterator) Reset() {
self.prev_ch = 0
self.prev_width = 0
self.current_width = 0
self.rune_count = 0
self.can_combine = false
self.seg = 0
self.parser.Reset()
}
func (self *WCWidthIterator) handle_csi(csi []byte) error {
if len(csi) > 1 && csi[len(csi)-1] == 'b' {
num_string := utils.UnsafeBytesToString(csi[:len(csi)-1])
n, err := strconv.Atoi(num_string)
if err == nil && n > 0 {
for i := 0; i < n; i++ {
err = self.handle_rune(self.prev_ch)
if err != nil {
return err
}
}
}
}
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
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 CharPropsFor(self.prev_ch).Is_emoji_presentation_base() == 1 && self.prev_width == 1 {
self.current_width += 1
self.prev_width = 2
}
case 0xfe0e:
if CharPropsFor(self.prev_ch).Is_emoji_presentation_base() == 1 && self.prev_width == 2 {
self.current_width -= 1
self.prev_width = 1
}
}
} 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
}
func (self *WCWidthIterator) ParseByte(b byte) (ans int) {
self.parser.ParseByte(b)
return self.current_width
}
func (self *WCWidthIterator) Parse(b []byte) (ans int) {
self.current_width = 0
self.parser.Parse(b)
return self.current_width
}
func (self *WCWidthIterator) CurrentWidth() int {
return self.current_width
}
func Stringwidth(text string) int {
w := CreateWCWidthIterator()
return w.Parse(utils.UnsafeStringToBytes(text))
}
func StripEscapeCodes(text string) string {
out := strings.Builder{}
out.Grow(len(text))
p := EscapeCodeParser{}
p.HandleRune = func(ch rune) error {
out.WriteRune(ch)
return nil
}
p.ParseString(text)
return out.String()
}
|