diff --git a/cmd/root.go b/cmd/root.go index 441287b..32ce390 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -84,6 +84,7 @@ func Execute() { func init() { rootCmd.PersistentFlags().BoolVarP(&lib.Debug, "debug", "d", false, "Enable debugging") rootCmd.PersistentFlags().BoolVarP(&lib.NoNumbering, "no-numbering", "n", false, "Disable header numbering") + rootCmd.PersistentFlags().BoolVarP(&lib.NoColor, "no-color", "N", false, "Disable pattern highlighting") rootCmd.PersistentFlags().BoolVarP(&lib.ShowVersion, "version", "V", false, "Print program version") rootCmd.PersistentFlags().BoolVarP(&lib.InvertMatch, "invert-match", "v", false, "select non-matching rows") rootCmd.PersistentFlags().BoolVarP(&ShowManual, "man", "m", false, "Display manual page") diff --git a/cmd/tablizer.go b/cmd/tablizer.go index 3e79bde..69c11d8 100644 --- a/cmd/tablizer.go +++ b/cmd/tablizer.go @@ -14,6 +14,7 @@ SYNOPSIS -v, --invert-match select non-matching rows -m, --man Display manual page -n, --no-numbering Disable header numbering + -N, --no-color Disable pattern highlighting -o, --output string Output mode - one of: orgtbl, markdown, extended, ascii(default) -X, --extended Enable extended output -M, --markdown Enable markdown table output @@ -70,6 +71,9 @@ DESCRIPTION The numbering can be suppressed by using the -n option. + By default, if a pattern has been speficied, matches will be + highlighted. You can disable this behavior with the -N option. + Finally the -d option enables debugging output which is mostly usefull for the developer. diff --git a/go.mod b/go.mod index 8b1c9ac..5568c4a 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,15 @@ go 1.18 require ( github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 + github.com/gookit/color v1.5.2 github.com/olekukonko/tablewriter v0.0.5 github.com/spf13/cobra v1.5.0 + github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 ) require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.8.0 // indirect + golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 // indirect ) diff --git a/go.sum b/go.sum index 6ec3e3c..58b6e4e 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= +github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= @@ -22,6 +24,10 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/lib/common.go b/lib/common.go index 8ea392f..e7fcdec 100644 --- a/lib/common.go +++ b/lib/common.go @@ -17,6 +17,11 @@ along with this program. If not, see . package lib +import ( + "github.com/gookit/color" + //"github.com/xo/terminfo" +) + var ( // command line flags Debug bool @@ -33,12 +38,37 @@ var ( OutflagShell bool OutputMode string InvertMatch bool + Pattern string + + /* + FIXME: make configurable somehow, config file or ENV + see https://github.com/gookit/color will be set by + io.ProcessFiles() according to currently supported + color mode. + */ + MatchFG string + MatchBG string + NoColor bool + + // colors to be used per supported color mode + Colors = map[color.Level]map[string]string{ + color.Level16: map[string]string{ + "bg": "green", "fg": "black", + }, + color.Level256: map[string]string{ + "bg": "lightGreen", "fg": "black", + }, + color.LevelRgb: map[string]string{ + // FIXME: maybe use something nicer + "bg": "lightGreen", "fg": "black", + }, + } // used for validation validOutputmodes = "(orgtbl|markdown|extended|ascii)" // main program version - Version = "v1.0.6" + Version = "v1.0.7" // generated version string, used by -v contains lib.Version on // main branch, and lib.Version-$branch-$lastcommit-$date on diff --git a/lib/helpers.go b/lib/helpers.go index f644da2..3c4c492 100644 --- a/lib/helpers.go +++ b/lib/helpers.go @@ -20,6 +20,8 @@ package lib import ( "errors" "fmt" + "github.com/gookit/color" + "os" "regexp" "strconv" "strings" @@ -51,7 +53,10 @@ func PrepareColumns() error { func numberizeHeaders(data *Tabdata) { // prepare headers: add numbers to headers numberedHeaders := []string{} + maxwidth := 0 // start from scratch, so we only look at displayed column widths + for i, head := range data.headers { + headlen := 0 if len(Columns) > 0 { // -c specified if !contains(UseColumns, i+1) { @@ -61,11 +66,21 @@ func numberizeHeaders(data *Tabdata) { } if NoNumbering { numberedHeaders = append(numberedHeaders, head) + headlen = len(head) } else { - numberedHeaders = append(numberedHeaders, fmt.Sprintf("%s(%d)", head, i+1)) + numhead := fmt.Sprintf("%s(%d)", head, i+1) + headlen = len(numhead) + numberedHeaders = append(numberedHeaders, numhead) + } + + if headlen > maxwidth { + maxwidth = headlen } } data.headers = numberedHeaders + if data.maxwidthHeader != maxwidth && maxwidth > 0 { + data.maxwidthHeader = maxwidth + } } func reduceColumns(data *Tabdata) { @@ -130,3 +145,21 @@ func trimRow(row []string) []string { return fixedrow } + +func colorizeData(output string) string { + if len(Pattern) > 0 && !NoColor && color.IsConsole(os.Stdout) { + r := regexp.MustCompile("(" + Pattern + ")") + return r.ReplaceAllString(output, "$1") + } else { + return output + } +} + +func isTerminal(f *os.File) bool { + o, _ := f.Stat() + if (o.Mode() & os.ModeCharDevice) == os.ModeCharDevice { + return true + } else { + return false + } +} diff --git a/lib/io.go b/lib/io.go index adab95b..913c9b2 100644 --- a/lib/io.go +++ b/lib/io.go @@ -19,6 +19,7 @@ package lib import ( "errors" + "github.com/gookit/color" "io" "os" ) @@ -26,6 +27,14 @@ import ( func ProcessFiles(args []string) error { fds, pattern, err := determineIO(args) + if !isTerminal(os.Stdout) { + color.Disable() + } else { + level := color.TermColorLevel() + MatchFG = Colors[level]["fg"] + MatchBG = Colors[level]["bg"] + } + if err != nil { return err } @@ -52,6 +61,7 @@ func determineIO(args []string) ([]io.Reader, string, error) { // first one is not a file, consider it as regexp and // shift arg list pattern = args[0] + Pattern = args[0] // FIXME args = args[1:] } diff --git a/lib/printer.go b/lib/printer.go index db0844d..6f3f76e 100644 --- a/lib/printer.go +++ b/lib/printer.go @@ -19,8 +19,8 @@ package lib import ( "fmt" + "github.com/gookit/color" "github.com/olekukonko/tablewriter" - "os" "regexp" "strings" ) @@ -76,14 +76,18 @@ func printOrgmodeData(data *Tabdata) { leftR := regexp.MustCompile("(?m)^\\+") rightR := regexp.MustCompile("\\+(?m)$") - fmt.Print(rightR.ReplaceAllString(leftR.ReplaceAllString(tableString.String(), "|"), "|")) + color.Print( + colorizeData( + rightR.ReplaceAllString( + leftR.ReplaceAllString(tableString.String(), "|"), "|"))) } /* Markdown table */ func printMarkdownData(data *Tabdata) { - table := tablewriter.NewWriter(os.Stdout) + tableString := &strings.Builder{} + table := tablewriter.NewWriter(tableString) table.SetHeader(data.headers) @@ -95,13 +99,15 @@ func printMarkdownData(data *Tabdata) { table.SetCenterSeparator("|") table.Render() + color.Print(colorizeData(tableString.String())) } /* Simple ASCII table without any borders etc, just like the input we expect */ func printAsciiData(data *Tabdata) { - table := tablewriter.NewWriter(os.Stdout) + tableString := &strings.Builder{} + table := tablewriter.NewWriter(tableString) table.SetHeader(data.headers) table.AppendBulk(data.entries) @@ -123,6 +129,7 @@ func printAsciiData(data *Tabdata) { table.SetNoWhiteSpace(true) table.Render() + color.Print(colorizeData(tableString.String())) } /* @@ -130,22 +137,14 @@ func printAsciiData(data *Tabdata) { */ func printExtendedData(data *Tabdata) { // needed for data output - format := fmt.Sprintf("%%%ds: %%s\n", data.maxwidthHeader) // FIXME: re-calculate if -c has been set + format := fmt.Sprintf("%%%ds: %%s\n", data.maxwidthHeader) if len(data.entries) > 0 { - var idx int for _, entry := range data.entries { - idx = 0 for i, value := range entry { - if len(Columns) > 0 { - if !contains(UseColumns, i+1) { - continue - } - } - - fmt.Printf(format, data.headers[idx], value) - idx++ + color.Printf(format, data.headers[i], value) } + fmt.Println() } } diff --git a/lib/printer_test.go b/lib/printer_test.go index 5ced090..a0b132f 100644 --- a/lib/printer_test.go +++ b/lib/printer_test.go @@ -19,6 +19,7 @@ package lib import ( "fmt" + "github.com/gookit/color" "os" "strings" "testing" @@ -62,8 +63,17 @@ asd igig cxxxncnc | 19191 | EDD 1 | X |`, "shell": `ONE="asd" TWO="igig" THREE="cxxxncnc" ONE="19191" TWO="EDD 1" THREE="X"`, + "extended": `ONE(1): asd + TWO(2): igig +THREE(3): cxxxncnc + + ONE(1): 19191 + TWO(2): EDD 1 +THREE(3): X`, } + NoColor = true + r, w, err := os.Pipe() if err != nil { t.Fatal(err) @@ -71,6 +81,9 @@ ONE="19191" TWO="EDD 1" THREE="X"`, origStdout := os.Stdout os.Stdout = w + // we need to tell the color mode the io.Writer, even if we don't usw colorization + color.SetOutput(w) + for mode, expect := range expects { testname := fmt.Sprintf("print-%s", mode) t.Run(testname, func(t *testing.T) { diff --git a/tablizer.1 b/tablizer.1 index c028cd4..05bf4d6 100644 --- a/tablizer.1 +++ b/tablizer.1 @@ -133,7 +133,7 @@ .\" ======================================================================== .\" .IX Title "TABLIZER 1" -.TH TABLIZER 1 "2022-10-05" "1" "User Commands" +.TH TABLIZER 1 "2022-10-10" "1" "User Commands" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -153,6 +153,7 @@ tablizer \- Manipulate tabular output of other programs \& \-v, \-\-invert\-match select non\-matching rows \& \-m, \-\-man Display manual page \& \-n, \-\-no\-numbering Disable header numbering +\& \-N, \-\-no\-color Disable pattern highlighting \& \-o, \-\-output string Output mode \- one of: orgtbl, markdown, extended, ascii(default) \& \-X, \-\-extended Enable extended output \& \-M, \-\-markdown Enable markdown table output @@ -218,6 +219,9 @@ the original order. .PP The numbering can be suppressed by using the \fB\-n\fR option. .PP +By default, if a \fBpattern\fR has been speficied, matches will be +highlighted. You can disable this behavior with the \fB\-N\fR option. +.PP Finally the \fB\-d\fR option enables debugging output which is mostly usefull for the developer. .SS "\s-1PATTERNS\s0" diff --git a/tablizer.pod b/tablizer.pod index 37c31ad..266b667 100644 --- a/tablizer.pod +++ b/tablizer.pod @@ -14,6 +14,7 @@ tablizer - Manipulate tabular output of other programs -v, --invert-match select non-matching rows -m, --man Display manual page -n, --no-numbering Disable header numbering + -N, --no-color Disable pattern highlighting -o, --output string Output mode - one of: orgtbl, markdown, extended, ascii(default) -X, --extended Enable extended output -M, --markdown Enable markdown table output @@ -74,6 +75,9 @@ the original order. The numbering can be suppressed by using the B<-n> option. +By default, if a B has been speficied, matches will be +highlighted. You can disable this behavior with the B<-N> option. + Finally the B<-d> option enables debugging output which is mostly usefull for the developer.