diff --git a/encode.go b/encode.go index 1bbc339..0be8d8c 100644 --- a/encode.go +++ b/encode.go @@ -46,6 +46,9 @@ type TableEncoder struct { // summary is the summary map. summary map[int]func(io.Writer, int) (int, error) + // isCustomSummary when summary has been set via options + isCustomSummary bool + // title is the title value. title *Value @@ -65,6 +68,10 @@ type TableEncoder struct { // They are at least as wide as user-supplied widths maxWidths []int + // maxWidth of whole table, before switching to the ExpandedEncoder, + // zero disables switching + maxWidth int + // scanCount is the number of scanned results in the result set. scanCount int @@ -170,29 +177,36 @@ func (enc *TableEncoder) Encode(w io.Writer) error { enc.calcWidth(vals) + if enc.maxWidth != 0 && enc.tableWidth() > enc.maxWidth { + t := *enc + t.formatter = NewEscapeFormatter() + exp := ExpandedEncoder{ + TableEncoder: t, + } + exp.offsets = make([]int, 2) + exp.maxWidths = make([]int, 2) + exp.calcWidth(vals) + + if err := exp.encodeVals(vals); err != nil { + return nil + } + continue + } + // print header if not already done if !wroteHeader { wroteHeader = true - enc.header() } - rs := enc.rowStyle(enc.lineStyle.Row) - // print buffered vals - for i := 0; i < len(vals); i++ { - enc.row(vals[i], rs) - if i+1%1000 == 0 { - // check error every 1k rows - if err := enc.w.Flush(); err != nil { - return err - } - } + if err := enc.encodeVals(vals); err != nil { + return err } - } - // draw end border - if enc.border >= 2 { - enc.divider(enc.rowStyle(enc.lineStyle.End)) + // draw end border + if enc.border >= 2 { + enc.divider(enc.rowStyle(enc.lineStyle.End)) + } } // add summary @@ -202,6 +216,22 @@ func (enc *TableEncoder) Encode(w io.Writer) error { return enc.w.Flush() } +func (enc *TableEncoder) encodeVals(vals [][]*Value) error { + rs := enc.rowStyle(enc.lineStyle.Row) + // print buffered vals + for i := 0; i < len(vals); i++ { + enc.row(vals[i], rs) + if i+1%1000 == 0 { + // check error every 1k rows + if err := enc.w.Flush(); err != nil { + return err + } + } + } + + return nil +} + // EncodeAll encodes all result sets to the writer using the encoder settings. func (enc *TableEncoder) EncodeAll(w io.Writer) error { var err error @@ -518,6 +548,9 @@ func (enc *TableEncoder) writeAligned(b, filler []byte, a Align, width, max int) // summarize writes the table scan count summary. func (enc *TableEncoder) summarize(w io.Writer) { // do summary + if enc.summary == nil { + return + } var f func(io.Writer, int) (int, error) if z, ok := enc.summary[-1]; ok { f = z @@ -544,6 +577,9 @@ func NewExpandedEncoder(resultSet ResultSet, opts ...Option) (Encoder, error) { } t := tableEnc.(*TableEncoder) t.formatter = NewEscapeFormatter() + if !t.isCustomSummary { + t.summary = nil + } enc := &ExpandedEncoder{ TableEncoder: *t, @@ -601,8 +637,6 @@ func (enc *ExpandedEncoder) Encode(w io.Writer) error { enc.calcWidth(vals) - rs := enc.rowStyle(enc.lineStyle.Row) - // print title if not already done if !wroteTitle && enc.title != nil { wroteTitle = true @@ -611,14 +645,27 @@ func (enc *ExpandedEncoder) Encode(w io.Writer) error { enc.w.Write(enc.newline) } - // print buffered vals - for i := 0; i < len(vals); i++ { - enc.record(i, vals[i], rs) - if i+1%1000 == 0 { - // check error every 1k rows - if err := enc.w.Flush(); err != nil { - return err - } + if err := enc.encodeVals(vals); err != nil { + return err + } + } + + // add summary + enc.summarize(w) + + // flush will return the error code + return enc.w.Flush() +} + +func (enc *ExpandedEncoder) encodeVals(vals [][]*Value) error { + rs := enc.rowStyle(enc.lineStyle.Row) + // print buffered vals + for i := 0; i < len(vals); i++ { + enc.record(i, vals[i], rs) + if i+1%1000 == 0 { + // check error every 1k rows + if err := enc.w.Flush(); err != nil { + return err } } } @@ -627,9 +674,7 @@ func (enc *ExpandedEncoder) Encode(w io.Writer) error { if enc.border >= 2 && enc.scanCount != 0 { enc.divider(enc.rowStyle(enc.lineStyle.End)) } - - // flush will return the error code - return enc.w.Flush() + return nil } // EncodeAll encodes all result sets to the writer using the encoder settings. diff --git a/go.mod b/go.mod index a26f4e4..ffe21c8 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,8 @@ module github.com/xo/tblfmt -require github.com/mattn/go-runewidth v0.0.9 +require ( + github.com/mattn/go-runewidth v0.0.9 + github.com/nathan-fiscaletti/consolesize-go v0.0.0-20210105204122-a87d9f614b9d +) go 1.13 diff --git a/go.sum b/go.sum index 4a94bf5..7905545 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,4 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/nathan-fiscaletti/consolesize-go v0.0.0-20210105204122-a87d9f614b9d h1:PQW4Aqovdqc9efHl9EVA+bhKmuZ4ME1HvSYYDvaDiK0= +github.com/nathan-fiscaletti/consolesize-go v0.0.0-20210105204122-a87d9f614b9d/go.mod h1:cxIIfNMTwff8f/ZvRouvWYF6wOoO7nj99neWSx2q/Es= diff --git a/opts.go b/opts.go index 7623677..1c54856 100644 --- a/opts.go +++ b/opts.go @@ -4,6 +4,8 @@ import ( "io" "strconv" "unicode/utf8" + + "github.com/nathan-fiscaletti/consolesize-go" ) // Builder is the shared builder interface. @@ -69,7 +71,8 @@ func FromMap(opts map[string]string) (Builder, []Option) { if e, ok := opts["expanded"]; ok { switch e { case "auto": - fallthrough + cols, _ := consolesize.GetConsoleSize() + tableOpts = append(tableOpts, WithMaxWidth(cols)) case "on": builder = NewExpandedEncoder } @@ -126,6 +129,7 @@ func WithSummary(summary map[int]func(io.Writer, int) (int, error)) Option { switch enc := v.(type) { case *TableEncoder: enc.summary = summary + enc.isCustomSummary = true } return nil } @@ -195,6 +199,19 @@ func WithWidths(widths []int) Option { } } +// WithMaxWidth is a encoder option to set maximum width before switching to expanded format. +func WithMaxWidth(w int) Option { + return func(v interface{}) error { + switch enc := v.(type) { + case *TableEncoder: + enc.maxWidth = w + case *ExpandedEncoder: + enc.maxWidth = w + } + return nil + } +} + // WithNewline is a encoder option to set the newline. func WithNewline(newline string) Option { return func(v interface{}) error { diff --git a/tblfmt_test.go b/tblfmt_test.go index 347e860..93376c1 100644 --- a/tblfmt_test.go +++ b/tblfmt_test.go @@ -235,6 +235,20 @@ func TestTinyAligned(t *testing.T) { } } +func TestWideExpanded(t *testing.T) { + resultSet := rswide() + buf := new(bytes.Buffer) + params := map[string]string{ + "format": "aligned", + "expanded": "auto", + "border": "2", + } + if err := EncodeAll(buf, resultSet, params); err != nil { + t.Fatalf("expected no error when encoding, got: %v", err) + } + t.Log("\n", newlineRE.ReplaceAllString(buf.String(), "\t")) +} + func TestBigAligned(t *testing.T) { resultSet := rsbig() diff --git a/util_test.go b/util_test.go index 51fcf7f..1ae043c 100644 --- a/util_test.go +++ b/util_test.go @@ -150,6 +150,28 @@ func rstiny() *rset { } } +func rswide() *rset { + return &rset{ + cols: []string{ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "cccccccccccccccccccccccccccccc", + "dddddddddddddddddddddddddddddd", + "eeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "ffffffffffffffffffffffffffffff", + "gggggggggggggggggggggggggggggg", + "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", + "iiiiiiiiiiiiiiiiiiiiiiiiiiiiii", + "jjjjjjjjjjjjjjjjjjjjjjjjjjjjjj", + }, + vals: [][][]interface{}{ + { + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}, + }, + }, + } +} + // rsset returns a predefined set of records for rs. func rsset(i int) [][]interface{} { return [][]interface{}{