utils.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package hhc_common
  2. import (
  3. "fmt"
  4. "github.com/cheynewallace/tabby"
  5. "github.com/mattn/go-runewidth"
  6. "github.com/ttacon/chalk"
  7. "io"
  8. "strings"
  9. "text/tabwriter"
  10. "unicode"
  11. )
  12. func FPrintHelp(w io.Writer, h []SDTHelpInfo) {
  13. sw := &strings.Builder{}
  14. t := tabby.NewCustom(tabwriter.NewWriter(sw, 0, 0, 2, ' ', 0))
  15. for i, v := range h {
  16. t.AddLine(i, v.Name, v.Description)
  17. }
  18. t.Print()
  19. ostr := sw.String()
  20. pcrlf := strings.ReplaceAll(ostr, "\n", "\r\n")
  21. _, _ = fmt.Fprint(w, pcrlf)
  22. }
  23. func GetUnicodeDeleteWidth(r rune) int {
  24. // Tested with ASCII non-control, Chinese, Japanese characters and Emoji.
  25. // I am not sure this is ok for all Unicode characters, so I made it as a dedicated function.
  26. return runewidth.RuneWidth(r)
  27. }
  28. type PrintableMessage struct {
  29. Text string
  30. Style chalk.Style
  31. }
  32. func splitStringInWidth(tw int, cll int, s string) []string {
  33. rstrl := []string{}
  34. nstr := strings.Builder{}
  35. lcc := cll
  36. for _, v := range []rune(s) {
  37. rw := runewidth.RuneWidth(v)
  38. if rw+lcc > tw {
  39. lcc = rw
  40. rstrl = append(rstrl, nstr.String())
  41. nstr = strings.Builder{}
  42. nstr.WriteRune(v)
  43. } else {
  44. lcc += rw
  45. nstr.WriteRune(v)
  46. }
  47. }
  48. if nstr.Len() > 0 {
  49. rstrl = append(rstrl, nstr.String())
  50. }
  51. return rstrl
  52. }
  53. func transToChalkedText(colorful bool, s string, styl chalk.Style) string {
  54. if colorful {
  55. if styl != nil {
  56. return styl.Style(s)
  57. }
  58. }
  59. return s
  60. }
  61. func PrintPrintablMessagesIntoLines(pm []PrintableMessage, termWidth int, colorful bool) []string {
  62. rlns := []string{}
  63. cln := strings.Builder{}
  64. clcnt := 0
  65. for _, v := range pm {
  66. sw := runewidth.StringWidth(v.Text)
  67. if sw+clcnt > termWidth {
  68. spll := splitStringInWidth(termWidth, clcnt, v.Text)
  69. for xis, vs := range spll {
  70. if xis == 0 {
  71. cln.WriteString(transToChalkedText(colorful, vs, v.Style))
  72. rlns = append(rlns, cln.String())
  73. cln = strings.Builder{}
  74. clcnt = 0
  75. } else {
  76. if xis == len(spll)-1 {
  77. cln.WriteString(transToChalkedText(colorful, vs, v.Style))
  78. clcnt += runewidth.StringWidth(vs)
  79. } else {
  80. rlns = append(rlns, transToChalkedText(colorful, vs, v.Style))
  81. }
  82. }
  83. }
  84. } else {
  85. clcnt += sw
  86. cln.WriteString(transToChalkedText(colorful, v.Text, v.Style))
  87. }
  88. if clcnt == termWidth {
  89. rlns = append(rlns, cln.String())
  90. cln = strings.Builder{}
  91. }
  92. }
  93. if cln.Len() > 0 {
  94. rlns = append(rlns, cln.String())
  95. }
  96. return rlns
  97. }
  98. func PrintStringIntoLines(s string, termWidth int) []string {
  99. if runewidth.StringWidth(s) <= termWidth {
  100. return []string{s}
  101. }
  102. rlns := []string{}
  103. cln := strings.Builder{}
  104. clcnt := 0
  105. ra := []rune(s)
  106. for _, v := range ra {
  107. rw := runewidth.RuneWidth(v)
  108. if rw+clcnt > termWidth {
  109. rlns = append(rlns, cln.String())
  110. cln = strings.Builder{}
  111. cln.WriteRune(v)
  112. clcnt = rw
  113. } else {
  114. cln.WriteRune(v)
  115. clcnt += rw
  116. }
  117. }
  118. if cln.Len() > 0 {
  119. rlns = append(rlns, cln.String())
  120. }
  121. return rlns
  122. }
  123. func TestRuneSlice(r1, r2 []rune) bool {
  124. if r1 == nil && r2 == nil {
  125. return true
  126. }
  127. if r1 == nil || r2 == nil {
  128. return false
  129. }
  130. if len(r1) != len(r2) {
  131. return false
  132. }
  133. for i := range r1 {
  134. if r1[i] != r2[i] {
  135. return false
  136. }
  137. }
  138. return true
  139. }
  140. func PrintRunesIntoLinesWithCursor(ra []rune, termWidth int, cursorPos int) (l []string, curY, curX int) {
  141. if runewidth.StringWidth(string(ra)) <= termWidth {
  142. return []string{string(ra)}, 0, cursorPos + 1
  143. }
  144. rlns := []string{}
  145. cln := strings.Builder{}
  146. clcnt := 0
  147. linecount := 0
  148. cpy, cpx := 0, 0
  149. hit := false
  150. for i, v := range ra {
  151. rw := runewidth.RuneWidth(v)
  152. if rw+clcnt > termWidth {
  153. rlns = append(rlns, cln.String())
  154. linecount++
  155. cln = strings.Builder{}
  156. cln.WriteRune(v)
  157. clcnt = rw
  158. } else {
  159. cln.WriteRune(v)
  160. clcnt += rw
  161. }
  162. if i == cursorPos {
  163. cpy = linecount
  164. cpx = clcnt
  165. hit = true
  166. }
  167. }
  168. if cln.Len() > 0 {
  169. rlns = append(rlns, cln.String())
  170. linecount++
  171. }
  172. if !hit {
  173. cpy = linecount - 1
  174. cpx = clcnt + 1
  175. }
  176. //fmt.Println("CPY=", cpy, "CPX=", cpx)
  177. return rlns, cpy, cpx
  178. }
  179. func FirstToUpper(s string) string {
  180. rs := []rune(s)
  181. if len(rs) > 0 {
  182. rs[0] = unicode.ToUpper(rs[0])
  183. }
  184. return string(rs)
  185. }
  186. func GetCompleteSuffix(cmd, prefix string) string {
  187. return strings.TrimPrefix(cmd, prefix) + " "
  188. }