wcswidth.go 2.9 KB

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