123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- package ztermtab
- import (
- "github.com/mattn/go-runewidth"
- "strings"
- )
- type TerminalTablePrinter struct {
- colsdef []columnDefine
- colCnt int
- schLock bool
- rowData [][]string
- termWidth int
- hasFreeLine bool
- freeLineIndex int
- split string
- splitWidth int
- }
- func NewTerminalTablePrinter(termWidth int, splitString string) *TerminalTablePrinter {
- ttp := &TerminalTablePrinter{
- colsdef: make([]columnDefine, 0),
- colCnt: 0,
- schLock: false,
- termWidth: termWidth,
- hasFreeLine: false,
- split: splitString,
- splitWidth: runewidth.StringWidth(splitString),
- }
- return ttp
- }
- func (p *TerminalTablePrinter) AddColumn(title string, minWidth int, maxWidth int, truncateOverflow bool) {
- if p.schLock {
- return
- }
- p.colsdef = append(p.colsdef, columnDefine{
- title: title,
- minWidth: minWidth,
- maxWidth: maxWidth,
- truncateOverflow: truncateOverflow,
- autoWidth: false,
- })
- p.colCnt++
- }
- func (p *TerminalTablePrinter) SetAutoWidthColumn(index int) {
- if p.schLock {
- return
- }
- if index >= p.colCnt {
- return
- }
- for i := range p.colsdef {
- if i == index {
- p.colsdef[i].autoWidth = true
- } else {
- p.colsdef[i].autoWidth = false
- }
- }
- p.freeLineIndex = index
- p.hasFreeLine = true
- }
- func (p *TerminalTablePrinter) ClearData() {
- p.schLock = true
- p.rowData = make([][]string, 0)
- for i := range p.colsdef {
- p.colsdef[i].currentWidth = p.colsdef[i].minWidth
- updateCurrentWidth(&p.colsdef[i], runewidth.StringWidth(p.colsdef[i].title))
- }
- }
- func (p *TerminalTablePrinter) AddRow(data []string) {
- if !p.schLock {
- p.ClearData()
- }
- line := make([]string, p.colCnt)
- for i, v := range data {
- if i >= p.colCnt {
- break
- }
- line[i] = v
- updateCurrentWidth(&p.colsdef[i], runewidth.StringWidth(v))
- }
- p.rowData = append(p.rowData, line)
- }
- func (p *TerminalTablePrinter) RenderToTerminalLines() []string {
- var fll int
- if p.hasFreeLine {
- nfll := 0
- for i, v := range p.colsdef {
- if !v.autoWidth {
- nfll += v.currentWidth
- }
- if i < p.colCnt-1 {
- nfll += p.splitWidth
- }
- }
- if nfll < p.termWidth {
- fll = p.termWidth - nfll
- } else {
- fll = 1
- }
- }
- colwidtab := make([]int, p.colCnt)
- for i, v := range p.colsdef {
- if v.autoWidth {
- colwidtab[i] = fll
- } else {
- colwidtab[i] = v.currentWidth
- }
- }
- out := make([]string, 0, len(p.rowData))
- for _, v := range p.rowData {
- maxline := 0
- sublines := make([][]string, len(v))
- for j, cell := range v {
- osl := printStringIntoLines(cell, colwidtab[j])
- if len(osl) > maxline {
- maxline = len(osl)
- }
- sublines[j] = osl
- }
- for k := 0; k < maxline; k++ {
- sb := strings.Builder{}
- for kj := 0; kj < p.colCnt; kj++ {
- kjv := sublines[kj]
- if k < len(kjv) {
- sb.WriteString(fillString(kjv[k], colwidtab[kj]))
- } else {
- sb.WriteString(strings.Repeat(" ", colwidtab[kj]))
- }
- if kj != p.colCnt-1 {
- sb.WriteString(p.split)
- }
- }
- out = append(out, sb.String())
- }
- }
- return out
- }
- func fillString(s string, fn int) string {
- l := runewidth.StringWidth(s)
- fill := fn - l
- sb := strings.Builder{}
- sb.WriteString(s)
- if fill > 0 {
- sb.WriteString(strings.Repeat(" ", fill))
- }
- return sb.String()
- }
- func updateCurrentWidth(cd *columnDefine, newwid int) {
- if newwid > cd.currentWidth {
- if newwid >= cd.maxWidth {
- cd.currentWidth = cd.maxWidth
- } else {
- cd.currentWidth = newwid
- }
- }
- }
- 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
- }
|