1
1
mirror of https://github.com/wader/fq.git synced 2024-11-26 21:55:57 +03:00
fq/internal/columnwriter/columnwriter.go
2021-09-12 13:08:50 +02:00

140 lines
2.1 KiB
Go

package columnwriter
import (
"bytes"
"io"
"strings"
"github.com/wader/fq/internal/ansi"
)
type Column struct {
Width int
Lines []string
Buf bytes.Buffer
Wrap bool
}
func divideString(s string, l int) []string {
var ss []string
parts := len(s) / l
for i := 0; i < parts; i++ {
ss = append(ss, s[i*l:(i+1)*l])
}
if len(s)%l != 0 {
ss = append(ss, s[parts*l:])
}
return ss
}
func (c *Column) Write(p []byte) (int, error) {
bb := &c.Buf
bb.Write(p)
b := bb.Bytes()
pos := 0
for {
i := indexByteSet(b[pos:], []byte{'\n'})
if i < 0 {
break
}
line := string([]rune(string(b[pos : pos+i])))
if c.Wrap && len(line) > c.Width {
c.Lines = append(c.Lines, divideString(line, c.Width)...)
} else {
c.Lines = append(c.Lines, line)
}
pos += i + 1
}
bb.Reset()
bb.Write(b[pos:])
return len(p), nil
}
func (c *Column) Flush() {
if c.Buf.Len() > 0 {
_, _ = c.Write([]byte{'\n'})
}
}
// Writer maintins multiple column io.Writer:s. On Flush() row align them.
type Writer struct {
Columns []*Column
w io.Writer
}
func indexByteSet(s []byte, cs []byte) int {
ri := -1
for _, c := range cs {
i := bytes.IndexByte(s, c)
if i != -1 && (ri == -1 || i < ri) {
ri = i
}
}
return ri
}
func New(w io.Writer, widths []int) *Writer {
var columns []*Column
for _, w := range widths {
columns = append(columns, &Column{Width: w})
}
return &Writer{
Columns: columns,
w: w,
}
}
func (w *Writer) Flush() error {
for _, c := range w.Columns {
c.Flush()
}
maxLines := 0
for _, c := range w.Columns {
lenLines := len(c.Lines)
if lenLines > maxLines {
maxLines = len(c.Lines)
}
}
for i := 0; i < maxLines; i++ {
for _, c := range w.Columns {
var s string
if i < len(c.Lines) {
s = c.Lines[i]
}
if c.Width != -1 {
l := ansi.Len(s)
if l < c.Width {
s += strings.Repeat(" ", c.Width-l)
}
}
if _, err := w.w.Write([]byte(s)); err != nil {
return err
}
}
if _, err := w.w.Write([]byte{'\n'}); err != nil {
return err
}
}
for _, c := range w.Columns {
c.Lines = nil
c.Buf.Reset()
}
return nil
}