diff --git a/Makefile b/Makefile index 5ce09dd..c2d28a5 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,7 @@ test: clean singletest: @echo "Call like this: 'make singletest TEST=TestPrepareColumns MOD=lib'" - go test -run $(TEST) github.com/tlinden/tablizer/$(MOD) + go test -run $(TEST) github.com/tlinden/tablizer/$(MOD) $(OPTS) cover-report: go test ./... -cover -coverprofile=coverage.out diff --git a/README.md b/README.md index 31888b7..8fcf288 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ Output Flags (mutually exclusive): -C, --csv Enable CSV output -A, --ascii Default output mode, ascii tabular -L, --hightlight-lines Use alternating background colors for tables + -y, --yank-columns Yank specified columns (separated by ,) to clipboard, + space separated Sort Mode Flags (mutually exclusive): -a, --sort-age sort according to age (duration) string diff --git a/cfg/config.go b/cfg/config.go index 93f026d..a92041e 100644 --- a/cfg/config.go +++ b/cfg/config.go @@ -28,7 +28,7 @@ import ( ) const DefaultSeparator string = `(\s\s+|\t)` -const Version string = "v1.3.2" +const Version string = "v1.3.3" const MAXPARTS = 2 var DefaultConfigfile = os.Getenv("HOME") + "/.config/tablizer/config" @@ -70,6 +70,8 @@ type Config struct { NoHeaders bool Columns string UseColumns []int + YankColumns string + UseYankColumns []int Separator string OutputMode int InvertMatch bool diff --git a/cmd/root.go b/cmd/root.go index 1d347db..3665de0 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -147,6 +147,8 @@ func Execute() { "Custom field separator") rootCmd.PersistentFlags().StringVarP(&conf.Columns, "columns", "c", "", "Only show the speficied columns (separated by ,)") + rootCmd.PersistentFlags().StringVarP(&conf.YankColumns, "yank-columns", "y", "", + "Yank the speficied columns (separated by ,) to the clipboard") rootCmd.PersistentFlags().StringVarP(&conf.TransposeColumns, "transpose-columns", "T", "", "Transpose the speficied columns (separated by ,)") diff --git a/cmd/tablizer.go b/cmd/tablizer.go index 7d6504b..2ead6d0 100644 --- a/cmd/tablizer.go +++ b/cmd/tablizer.go @@ -30,6 +30,8 @@ SYNOPSIS -C, --csv Enable CSV output -A, --ascii Default output mode, ascii tabular -L, --hightlight-lines Use alternating background colors for tables + -y, --yank-columns Yank specified columns (separated by ,) to clipboard, + space separated Sort Mode Flags (mutually exclusive): -a, --sort-age sort according to age (duration) string @@ -284,6 +286,18 @@ DESCRIPTION markdown which prints a Markdown table, yaml, which prints yaml encoding and CSV mode, which prints a comma separated value file. + PUT FIELDS TO CLIPBOARD + You can let tablizer put fields to the clipboard using the option "-y". + This best fits the use-case when the result of your filtering yields + just one row. For example: + + cloudctl cluster ls | tablizer -yid matchbox + + If "matchbox" matches one cluster, you can immediately use the id of + that cluster somewhere else and paste it. Of course, if there are + multiple matches, then all id's will be put into the clipboard separated + by one space. + ENVIRONMENT VARIABLES tablizer supports certain environment variables which use can use to influence program behavior. Commandline flags have always precedence @@ -433,6 +447,8 @@ Output Flags (mutually exclusive): -C, --csv Enable CSV output -A, --ascii Default output mode, ascii tabular -L, --hightlight-lines Use alternating background colors for tables + -y, --yank-columns Yank specified columns (separated by ,) to clipboard, + space separated Sort Mode Flags (mutually exclusive): -a, --sort-age sort according to age (duration) string diff --git a/go.mod b/go.mod index 7c203d7..3cf9830 100644 --- a/go.mod +++ b/go.mod @@ -22,8 +22,10 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/tiagomelo/go-clipboard v0.1.2 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/zclconf/go-cty v1.13.3 // indirect golang.org/x/mod v0.18.0 // indirect diff --git a/go.sum b/go.sum index 5491a3d..6736f35 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,8 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzC github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -49,6 +51,12 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tiagomelo/go-clipboard v0.1.1 h1:nddQ5DsEnKW0KdzTILhbLpSq3e9y2dkJXEOtsMs6H7A= +github.com/tiagomelo/go-clipboard v0.1.1/go.mod h1:kXtjJBIMimZaGbxmcKZ8+JqK+acSNf5tAJiChlZBOr8= +github.com/tiagomelo/go-clipboard v0.1.2-0.20250126153310-fcc1f95408cf h1:csb/+rmJBAtOP6OP+9soTnwJVuhlUpedjb5dPlNZasY= +github.com/tiagomelo/go-clipboard v0.1.2-0.20250126153310-fcc1f95408cf/go.mod h1:kXtjJBIMimZaGbxmcKZ8+JqK+acSNf5tAJiChlZBOr8= +github.com/tiagomelo/go-clipboard v0.1.2 h1:Ph2icR0vZRIj3v5ExvsGweBwsbbDUTlS6HoF40MkQD8= +github.com/tiagomelo/go-clipboard v0.1.2/go.mod h1:kXtjJBIMimZaGbxmcKZ8+JqK+acSNf5tAJiChlZBOr8= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= diff --git a/lib/helpers.go b/lib/helpers.go index c2ae9e9..5a365e4 100644 --- a/lib/helpers.go +++ b/lib/helpers.go @@ -77,6 +77,14 @@ func PrepareColumns(conf *cfg.Config, data *Tabdata) error { conf.UseColumns = usecolumns + // -y columns + useyankcolumns, err := PrepareColumnVars(conf.YankColumns, data) + if err != nil { + return err + } + + conf.UseYankColumns = useyankcolumns + return nil } diff --git a/lib/printer.go b/lib/printer.go index 3189060..0041f85 100644 --- a/lib/printer.go +++ b/lib/printer.go @@ -1,5 +1,5 @@ /* -Copyright © 2022 Thomas von Dein +Copyright © 2022-2025 Thomas von Dein This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,6 +38,9 @@ func printData(writer io.Writer, conf cfg.Config, data *Tabdata) { // by, independently if it's being used for display or not. sortTable(conf, data) + // put one or more columns into clipboard + yankColumns(conf, data) + // add numbers to headers and remove those we're not interested in numberizeAndReduceHeaders(conf, data) diff --git a/lib/yank.go b/lib/yank.go new file mode 100644 index 0000000..39265d3 --- /dev/null +++ b/lib/yank.go @@ -0,0 +1,51 @@ +/* +Copyright © 2022-2025 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package lib + +import ( + "log" + "strings" + + "github.com/tiagomelo/go-clipboard/clipboard" + "github.com/tlinden/tablizer/cfg" +) + +func yankColumns(conf cfg.Config, data *Tabdata) { + var yank []string + + if len(data.entries) == 0 || len(conf.UseYankColumns) == 0 { + return + } + + for _, row := range data.entries { + for i, field := range row { + for _, idx := range conf.UseYankColumns { + if i == idx-1 { + yank = append(yank, field) + } + } + } + } + + if len(yank) > 0 { + cb := clipboard.New(clipboard.ClipboardOptions{Primary: true}) + if err := cb.CopyText(strings.Join(yank, " ")); err != nil { + log.Fatalln("error writing string to clipboard:", err) + } + } +} diff --git a/lib/yank_test.go b/lib/yank_test.go new file mode 100644 index 0000000..748104b --- /dev/null +++ b/lib/yank_test.go @@ -0,0 +1,72 @@ +/* +Copyright © 2025 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package lib + +import ( + "bytes" + "fmt" + "testing" + + "github.com/tiagomelo/go-clipboard/clipboard" + "github.com/tlinden/tablizer/cfg" +) + +var yanktests = []struct { + name string + yank []int // -y$colum,$column... after processing + filter string + expect string +}{ + { + name: "one", + yank: []int{1}, + filter: "beta", + }, +} + +func DISABLED_TestYankColumns(t *testing.T) { + cb := clipboard.New() + + for _, testdata := range yanktests { + testname := fmt.Sprintf("yank-%s-filter-%s", + testdata.name, testdata.filter) + t.Run(testname, func(t *testing.T) { + conf := cfg.Config{ + OutputMode: cfg.ASCII, + UseYankColumns: testdata.yank, + NoColor: true, + } + + conf.ApplyDefaults() + data := newData() // defined in printer_test.go, reused here + + var writer bytes.Buffer + printData(&writer, conf, &data) + + got, err := cb.PasteText() + if err != nil { + t.Errorf("failed to fetch yanked text from clipboard") + } + + if got != testdata.expect { + t.Errorf("not yanked correctly:\n+++ got:\n%s\n+++ want:\n%s", + got, testdata.expect) + } + }) + } +} diff --git a/tablizer.1 b/tablizer.1 index 4bc9cb1..b5d4571 100644 --- a/tablizer.1 +++ b/tablizer.1 @@ -133,7 +133,8 @@ .\" ======================================================================== .\" .IX Title "TABLIZER 1" -.TH TABLIZER 1 "2025-01-30" "1" "User Commands" +.TH TABLIZER 1 "2025-02-23" "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 @@ -168,6 +169,8 @@ tablizer \- Manipulate tabular output of other programs \& \-C, \-\-csv Enable CSV output \& \-A, \-\-ascii Default output mode, ascii tabular \& \-L, \-\-hightlight\-lines Use alternating background colors for tables +\& \-y, \-\-yank\-columns Yank specified columns (separated by ,) to clipboard, +\& space separated \& \& Sort Mode Flags (mutually exclusive): \& \-a, \-\-sort\-age sort according to age (duration) string @@ -462,6 +465,20 @@ more output modes available: \fBorgtbl\fR which prints an Emacs org-mode table and \fBmarkdown\fR which prints a Markdown table, \fByaml\fR, which prints yaml encoding and \s-1CSV\s0 mode, which prints a comma separated value file. +.SS "\s-1PUT FIELDS TO CLIPBOARD\s0" +.IX Subsection "PUT FIELDS TO CLIPBOARD" +You can let tablizer put fields to the clipboard using the option +\&\f(CW\*(C`\-y\*(C'\fR. This best fits the use-case when the result of your filtering +yields just one row. For example: +.PP +.Vb 1 +\& cloudctl cluster ls | tablizer \-yid matchbox +.Ve +.PP +If \*(L"matchbox\*(R" matches one cluster, you can immediately use the id of +that cluster somewhere else and paste it. Of course, if there are +multiple matches, then all id's will be put into the clipboard +separated by one space. .SS "\s-1ENVIRONMENT VARIABLES\s0" .IX Subsection "ENVIRONMENT VARIABLES" \&\fBtablizer\fR supports certain environment variables which use can use diff --git a/tablizer.pod b/tablizer.pod index 5ebfc3d..e21c0b2 100644 --- a/tablizer.pod +++ b/tablizer.pod @@ -29,6 +29,8 @@ tablizer - Manipulate tabular output of other programs -C, --csv Enable CSV output -A, --ascii Default output mode, ascii tabular -L, --hightlight-lines Use alternating background colors for tables + -y, --yank-columns Yank specified columns (separated by ,) to clipboard, + space separated Sort Mode Flags (mutually exclusive): -a, --sort-age sort according to age (duration) string @@ -306,6 +308,19 @@ table and B which prints a Markdown table, B, which prints yaml encoding and CSV mode, which prints a comma separated value file. +=head2 PUT FIELDS TO CLIPBOARD + +You can let tablizer put fields to the clipboard using the option +C<-y>. This best fits the use-case when the result of your filtering +yields just one row. For example: + + cloudctl cluster ls | tablizer -yid matchbox + +If "matchbox" matches one cluster, you can immediately use the id of +that cluster somewhere else and paste it. Of course, if there are +multiple matches, then all id's will be put into the clipboard +separated by one space. + =head2 ENVIRONMENT VARIABLES B supports certain environment variables which use can use