package hhc_common import ( "fmt" "github.com/cheynewallace/tabby" "github.com/mattn/go-runewidth" "github.com/ttacon/chalk" "io" "strings" "text/tabwriter" "unicode" ) func FPrintHelp(w io.Writer, h []SDTHelpInfo) { sw := &strings.Builder{} t := tabby.NewCustom(tabwriter.NewWriter(sw, 0, 0, 2, ' ', 0)) for i, v := range h { t.AddLine(i, v.Name, v.Description) } t.Print() ostr := sw.String() pcrlf := strings.ReplaceAll(ostr, "\n", "\r\n") _, _ = fmt.Fprint(w, pcrlf) } func GetUnicodeDeleteWidth(r rune) int { // Tested with ASCII non-control, Chinese, Japanese characters and Emoji. // I am not sure this is ok for all Unicode characters, so I made it as a dedicated function. return runewidth.RuneWidth(r) } type PrintableMessage struct { Text string Style chalk.Style } func splitStringInWidth(tw int, cll int, s string) []string { rstrl := []string{} nstr := strings.Builder{} lcc := cll for _, v := range []rune(s) { rw := runewidth.RuneWidth(v) if rw+lcc > tw { lcc = rw rstrl = append(rstrl, nstr.String()) nstr = strings.Builder{} nstr.WriteRune(v) } else { lcc += rw nstr.WriteRune(v) } } if nstr.Len() > 0 { rstrl = append(rstrl, nstr.String()) } return rstrl } func transToChalkedText(colorful bool, s string, styl chalk.Style) string { if colorful { if styl != nil { return styl.Style(s) } } return s } func PrintPrintablMessagesIntoLines(pm []PrintableMessage, termWidth int, colorful bool) []string { rlns := []string{} cln := strings.Builder{} clcnt := 0 for _, v := range pm { sw := runewidth.StringWidth(v.Text) if sw+clcnt > termWidth { spll := splitStringInWidth(termWidth, clcnt, v.Text) for xis, vs := range spll { if xis == 0 { cln.WriteString(transToChalkedText(colorful, vs, v.Style)) rlns = append(rlns, cln.String()) cln = strings.Builder{} clcnt = 0 } else { if xis == len(spll)-1 { cln.WriteString(transToChalkedText(colorful, vs, v.Style)) clcnt += runewidth.StringWidth(vs) } else { rlns = append(rlns, transToChalkedText(colorful, vs, v.Style)) } } } } else { clcnt += sw cln.WriteString(transToChalkedText(colorful, v.Text, v.Style)) } if clcnt == termWidth { rlns = append(rlns, cln.String()) cln = strings.Builder{} } } if cln.Len() > 0 { rlns = append(rlns, cln.String()) } return rlns } func PrintStringIntoLines(s string, termWidth int) []string { if runewidth.StringWidth(s) <= termWidth { return []string{s} } rlns := []string{} cln := strings.Builder{} clcnt := 0 ra := []rune(s) for _, v := range ra { rw := runewidth.RuneWidth(v) if rw+clcnt > termWidth { rlns = append(rlns, cln.String()) cln = strings.Builder{} cln.WriteRune(v) clcnt = rw } else { cln.WriteRune(v) clcnt += rw } } if cln.Len() > 0 { rlns = append(rlns, cln.String()) } return rlns } func TestRuneSlice(r1, r2 []rune) bool { if r1 == nil && r2 == nil { return true } if r1 == nil || r2 == nil { return false } if len(r1) != len(r2) { return false } for i := range r1 { if r1[i] != r2[i] { return false } } return true } func PrintRunesIntoLinesWithCursor(ra []rune, termWidth int, cursorPos int) (l []string, curY, curX int) { if runewidth.StringWidth(string(ra)) <= termWidth { return []string{string(ra)}, 0, cursorPos + 1 } rlns := []string{} cln := strings.Builder{} clcnt := 0 linecount := 0 cpy, cpx := 0, 0 hit := false for i, v := range ra { rw := runewidth.RuneWidth(v) if rw+clcnt > termWidth { rlns = append(rlns, cln.String()) linecount++ cln = strings.Builder{} cln.WriteRune(v) clcnt = rw } else { cln.WriteRune(v) clcnt += rw } if i == cursorPos { cpy = linecount cpx = clcnt hit = true } } if cln.Len() > 0 { rlns = append(rlns, cln.String()) linecount++ } if !hit { cpy = linecount - 1 cpx = clcnt + 1 } //fmt.Println("CPY=", cpy, "CPX=", cpx) return rlns, cpy, cpx } func FirstToUpper(s string) string { rs := []rune(s) if len(rs) > 0 { rs[0] = unicode.ToUpper(rs[0]) } return string(rs) } func GetCompleteSuffix(cmd, prefix string) string { return strings.TrimPrefix(cmd, prefix) + " " }