mirror of
https://codeberg.org/scip/tablizer.git
synced 2025-12-17 04:30:56 +01:00
added -k parameter to sort by columns
This commit is contained in:
@@ -90,6 +90,7 @@ func init() {
|
|||||||
rootCmd.PersistentFlags().BoolVarP(&ShowManual, "man", "m", false, "Display manual page")
|
rootCmd.PersistentFlags().BoolVarP(&ShowManual, "man", "m", false, "Display manual page")
|
||||||
rootCmd.PersistentFlags().StringVarP(&lib.Separator, "separator", "s", lib.DefaultSeparator, "Custom field separator")
|
rootCmd.PersistentFlags().StringVarP(&lib.Separator, "separator", "s", lib.DefaultSeparator, "Custom field separator")
|
||||||
rootCmd.PersistentFlags().StringVarP(&lib.Columns, "columns", "c", "", "Only show the speficied columns (separated by ,)")
|
rootCmd.PersistentFlags().StringVarP(&lib.Columns, "columns", "c", "", "Only show the speficied columns (separated by ,)")
|
||||||
|
rootCmd.PersistentFlags().IntVarP(&lib.SortByColumn, "sort-by", "k", 0, "Sort by column (default: 1)")
|
||||||
|
|
||||||
// output flags, only 1 allowed, hidden, since just short cuts
|
// output flags, only 1 allowed, hidden, since just short cuts
|
||||||
rootCmd.PersistentFlags().BoolVarP(&lib.OutflagExtended, "extended", "X", false, "Enable extended output")
|
rootCmd.PersistentFlags().BoolVarP(&lib.OutflagExtended, "extended", "X", false, "Enable extended output")
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ SYNOPSIS
|
|||||||
-M, --markdown Enable markdown table output
|
-M, --markdown Enable markdown table output
|
||||||
-O, --orgtbl Enable org-mode table output
|
-O, --orgtbl Enable org-mode table output
|
||||||
-s, --separator string Custom field separator
|
-s, --separator string Custom field separator
|
||||||
|
-k, --sort-by int Sort by column (default: 1)
|
||||||
-v, --version Print program version
|
-v, --version Print program version
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
@@ -74,6 +75,10 @@ DESCRIPTION
|
|||||||
By default, if a pattern has been speficied, matches will be
|
By default, if a pattern has been speficied, matches will be
|
||||||
highlighted. You can disable this behavior with the -N option.
|
highlighted. You can disable this behavior with the -N option.
|
||||||
|
|
||||||
|
Use the -k option to specify by which column to sort the tabular data
|
||||||
|
(as in GNU sort(1)). The default sort column is the first one. To
|
||||||
|
disable sorting at all, supply 0 (Zero) to -k.
|
||||||
|
|
||||||
Finally the -d option enables debugging output which is mostly usefull
|
Finally the -d option enables debugging output which is mostly usefull
|
||||||
for the developer.
|
for the developer.
|
||||||
|
|
||||||
|
|||||||
@@ -68,10 +68,22 @@ var (
|
|||||||
validOutputmodes = "(orgtbl|markdown|extended|ascii)"
|
validOutputmodes = "(orgtbl|markdown|extended|ascii)"
|
||||||
|
|
||||||
// main program version
|
// main program version
|
||||||
Version = "v1.0.7"
|
Version = "v1.0.8"
|
||||||
|
|
||||||
// generated version string, used by -v contains lib.Version on
|
// generated version string, used by -v contains lib.Version on
|
||||||
// main branch, and lib.Version-$branch-$lastcommit-$date on
|
// main branch, and lib.Version-$branch-$lastcommit-$date on
|
||||||
// development branch
|
// development branch
|
||||||
VERSION string
|
VERSION string
|
||||||
|
|
||||||
|
// sorting
|
||||||
|
SortByColumn int
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// contains a whole parsed table
|
||||||
|
type Tabdata struct {
|
||||||
|
maxwidthHeader int // longest header
|
||||||
|
maxwidthPerCol []int // max width per column
|
||||||
|
columns int // count
|
||||||
|
headers []string // [ "ID", "NAME", ...]
|
||||||
|
entries [][]string
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,15 +27,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// contains a whole parsed table
|
|
||||||
type Tabdata struct {
|
|
||||||
maxwidthHeader int // longest header
|
|
||||||
maxwidthPerCol []int // max width per column
|
|
||||||
columns int // count
|
|
||||||
headers []string // [ "ID", "NAME", ...]
|
|
||||||
entries [][]string
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Parse tabular input.
|
Parse tabular input.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -26,11 +26,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func printData(data *Tabdata) {
|
func printData(data *Tabdata) {
|
||||||
|
// some output preparations:
|
||||||
|
|
||||||
if OutputMode != "shell" {
|
if OutputMode != "shell" {
|
||||||
|
// not needed in eval string
|
||||||
numberizeHeaders(data)
|
numberizeHeaders(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove unwanted columns, if any
|
||||||
reduceColumns(data)
|
reduceColumns(data)
|
||||||
|
|
||||||
|
// sort the data
|
||||||
|
sortTable(data, SortByColumn)
|
||||||
|
|
||||||
switch OutputMode {
|
switch OutputMode {
|
||||||
case "extended":
|
case "extended":
|
||||||
printExtendedData(data)
|
printExtendedData(data)
|
||||||
|
|||||||
@@ -25,6 +25,20 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func stdout2pipe(t *testing.T) (*os.File, *os.File) {
|
||||||
|
reader, writer, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
origStdout := os.Stdout
|
||||||
|
os.Stdout = writer
|
||||||
|
|
||||||
|
// we need to tell the color mode the io.Writer, even if we don't usw colorization
|
||||||
|
color.SetOutput(writer)
|
||||||
|
|
||||||
|
return origStdout, reader
|
||||||
|
}
|
||||||
|
|
||||||
func TestPrinter(t *testing.T) {
|
func TestPrinter(t *testing.T) {
|
||||||
startdata := Tabdata{
|
startdata := Tabdata{
|
||||||
maxwidthHeader: 5,
|
maxwidthHeader: 5,
|
||||||
@@ -51,18 +65,22 @@ func TestPrinter(t *testing.T) {
|
|||||||
"ascii": `ONE(1) TWO(2) THREE(3)
|
"ascii": `ONE(1) TWO(2) THREE(3)
|
||||||
asd igig cxxxncnc
|
asd igig cxxxncnc
|
||||||
19191 EDD 1 X`,
|
19191 EDD 1 X`,
|
||||||
|
|
||||||
"orgtbl": `|--------+--------+----------|
|
"orgtbl": `|--------+--------+----------|
|
||||||
| ONE(1) | TWO(2) | THREE(3) |
|
| ONE(1) | TWO(2) | THREE(3) |
|
||||||
|--------+--------+----------|
|
|--------+--------+----------|
|
||||||
| asd | igig | cxxxncnc |
|
| asd | igig | cxxxncnc |
|
||||||
| 19191 | EDD 1 | X |
|
| 19191 | EDD 1 | X |
|
||||||
|--------+--------+----------|`,
|
|--------+--------+----------|`,
|
||||||
|
|
||||||
"markdown": `| ONE(1) | TWO(2) | THREE(3) |
|
"markdown": `| ONE(1) | TWO(2) | THREE(3) |
|
||||||
|--------|--------|----------|
|
|--------|--------|----------|
|
||||||
| asd | igig | cxxxncnc |
|
| asd | igig | cxxxncnc |
|
||||||
| 19191 | EDD 1 | X |`,
|
| 19191 | EDD 1 | X |`,
|
||||||
|
|
||||||
"shell": `ONE="asd" TWO="igig" THREE="cxxxncnc"
|
"shell": `ONE="asd" TWO="igig" THREE="cxxxncnc"
|
||||||
ONE="19191" TWO="EDD 1" THREE="X"`,
|
ONE="19191" TWO="EDD 1" THREE="X"`,
|
||||||
|
|
||||||
"extended": `ONE(1): asd
|
"extended": `ONE(1): asd
|
||||||
TWO(2): igig
|
TWO(2): igig
|
||||||
THREE(3): cxxxncnc
|
THREE(3): cxxxncnc
|
||||||
@@ -73,16 +91,9 @@ THREE(3): X`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
NoColor = true
|
NoColor = true
|
||||||
|
SortByColumn = 0 // disable sorting
|
||||||
|
|
||||||
r, w, err := os.Pipe()
|
origStdout, reader := stdout2pipe(t)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
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 {
|
for mode, expect := range expects {
|
||||||
testname := fmt.Sprintf("print-%s", mode)
|
testname := fmt.Sprintf("print-%s", mode)
|
||||||
@@ -93,7 +104,7 @@ THREE(3): X`,
|
|||||||
printData(&data)
|
printData(&data)
|
||||||
|
|
||||||
buf := make([]byte, 1024)
|
buf := make([]byte, 1024)
|
||||||
n, err := r.Read(buf)
|
n, err := reader.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -101,7 +112,8 @@ THREE(3): X`,
|
|||||||
output := strings.TrimSpace(string(buf))
|
output := strings.TrimSpace(string(buf))
|
||||||
|
|
||||||
if output != expect {
|
if output != expect {
|
||||||
t.Errorf("output mode: %s, got:\n%s\nwant:\n%s\n (%d <=> %d)", mode, output, expect, len(output), len(expect))
|
t.Errorf("output mode: %s, got:\n%s\nwant:\n%s\n (%d <=> %d)",
|
||||||
|
mode, output, expect, len(output), len(expect))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -110,3 +122,91 @@ THREE(3): X`,
|
|||||||
os.Stdout = origStdout
|
os.Stdout = origStdout
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSortPrinter(t *testing.T) {
|
||||||
|
startdata := Tabdata{
|
||||||
|
maxwidthHeader: 5,
|
||||||
|
maxwidthPerCol: []int{
|
||||||
|
3,
|
||||||
|
3,
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
columns: 3,
|
||||||
|
headers: []string{
|
||||||
|
"ONE", "TWO", "THREE",
|
||||||
|
},
|
||||||
|
entries: [][]string{
|
||||||
|
[]string{
|
||||||
|
"abc", "345", "b1",
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
"bcd", "234", "a2",
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
"cde", "123", "c3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
data Tabdata
|
||||||
|
sortby int
|
||||||
|
expect string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
data: startdata,
|
||||||
|
sortby: 1,
|
||||||
|
expect: `ONE(1) TWO(2) THREE(3)
|
||||||
|
abc 345 b1
|
||||||
|
bcd 234 a2
|
||||||
|
cde 123 c3`,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
data: startdata,
|
||||||
|
sortby: 2,
|
||||||
|
expect: `ONE(1) TWO(2) THREE(3)
|
||||||
|
cde 123 c3
|
||||||
|
bcd 234 a2
|
||||||
|
abc 345 b1`,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
data: startdata,
|
||||||
|
sortby: 3,
|
||||||
|
expect: `ONE(1) TWO(2) THREE(3)
|
||||||
|
bcd 234 a2
|
||||||
|
abc 345 b1
|
||||||
|
cde 123 c3`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
NoColor = true
|
||||||
|
OutputMode = "ascii"
|
||||||
|
origStdout, reader := stdout2pipe(t)
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
testname := fmt.Sprintf("print-sorted-table-%d", tt.sortby)
|
||||||
|
t.Run(testname, func(t *testing.T) {
|
||||||
|
SortByColumn = tt.sortby
|
||||||
|
|
||||||
|
printData(&tt.data)
|
||||||
|
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
n, err := reader.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
buf = buf[:n]
|
||||||
|
output := strings.TrimSpace(string(buf))
|
||||||
|
|
||||||
|
if output != tt.expect {
|
||||||
|
t.Errorf("sort column: %d, got:\n%s\nwant:\n%s",
|
||||||
|
tt.sortby, output, tt.expect)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore
|
||||||
|
os.Stdout = origStdout
|
||||||
|
}
|
||||||
|
|||||||
46
lib/sort.go
Normal file
46
lib/sort.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sortTable(data *Tabdata, col int) {
|
||||||
|
if col <= 0 {
|
||||||
|
// no sorting wanted
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
col-- // ui starts counting by 1, but use 0 internally
|
||||||
|
|
||||||
|
// sanity checks
|
||||||
|
if len(data.entries) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if col >= len(data.headers) {
|
||||||
|
// fall back to default column
|
||||||
|
col = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// actual sorting
|
||||||
|
sort.SliceStable(data.entries, func(i, j int) bool {
|
||||||
|
return data.entries[i][col] < data.entries[j][col]
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
.\" ========================================================================
|
.\" ========================================================================
|
||||||
.\"
|
.\"
|
||||||
.IX Title "TABLIZER 1"
|
.IX Title "TABLIZER 1"
|
||||||
.TH TABLIZER 1 "2022-10-10" "1" "User Commands"
|
.TH TABLIZER 1 "2022-10-13" "1" "User Commands"
|
||||||
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
|
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
|
||||||
.\" way too many mistakes in technical documents.
|
.\" way too many mistakes in technical documents.
|
||||||
.if n .ad l
|
.if n .ad l
|
||||||
@@ -159,6 +159,7 @@ tablizer \- Manipulate tabular output of other programs
|
|||||||
\& \-M, \-\-markdown Enable markdown table output
|
\& \-M, \-\-markdown Enable markdown table output
|
||||||
\& \-O, \-\-orgtbl Enable org\-mode table output
|
\& \-O, \-\-orgtbl Enable org\-mode table output
|
||||||
\& \-s, \-\-separator string Custom field separator
|
\& \-s, \-\-separator string Custom field separator
|
||||||
|
\& \-k, \-\-sort\-by int Sort by column (default: 1)
|
||||||
\& \-v, \-\-version Print program version
|
\& \-v, \-\-version Print program version
|
||||||
.Ve
|
.Ve
|
||||||
.SH "DESCRIPTION"
|
.SH "DESCRIPTION"
|
||||||
@@ -222,6 +223,10 @@ The numbering can be suppressed by using the \fB\-n\fR option.
|
|||||||
By default, if a \fBpattern\fR has been speficied, matches will be
|
By default, if a \fBpattern\fR has been speficied, matches will be
|
||||||
highlighted. You can disable this behavior with the \fB\-N\fR option.
|
highlighted. You can disable this behavior with the \fB\-N\fR option.
|
||||||
.PP
|
.PP
|
||||||
|
Use the \fB\-k\fR option to specify by which column to sort the tabular
|
||||||
|
data (as in \s-1GNU\s0 \fBsort\fR\|(1)). The default sort column is the first one. To
|
||||||
|
disable sorting at all, supply 0 (Zero) to \-k.
|
||||||
|
.PP
|
||||||
Finally the \fB\-d\fR option enables debugging output which is mostly
|
Finally the \fB\-d\fR option enables debugging output which is mostly
|
||||||
usefull for the developer.
|
usefull for the developer.
|
||||||
.SS "\s-1PATTERNS\s0"
|
.SS "\s-1PATTERNS\s0"
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ tablizer - Manipulate tabular output of other programs
|
|||||||
-M, --markdown Enable markdown table output
|
-M, --markdown Enable markdown table output
|
||||||
-O, --orgtbl Enable org-mode table output
|
-O, --orgtbl Enable org-mode table output
|
||||||
-s, --separator string Custom field separator
|
-s, --separator string Custom field separator
|
||||||
|
-k, --sort-by int Sort by column (default: 1)
|
||||||
-v, --version Print program version
|
-v, --version Print program version
|
||||||
|
|
||||||
|
|
||||||
@@ -78,6 +79,10 @@ The numbering can be suppressed by using the B<-n> option.
|
|||||||
By default, if a B<pattern> has been speficied, matches will be
|
By default, if a B<pattern> has been speficied, matches will be
|
||||||
highlighted. You can disable this behavior with the B<-N> option.
|
highlighted. You can disable this behavior with the B<-N> option.
|
||||||
|
|
||||||
|
Use the B<-k> option to specify by which column to sort the tabular
|
||||||
|
data (as in GNU sort(1)). The default sort column is the first one. To
|
||||||
|
disable sorting at all, supply 0 (Zero) to -k.
|
||||||
|
|
||||||
Finally the B<-d> option enables debugging output which is mostly
|
Finally the B<-d> option enables debugging output which is mostly
|
||||||
usefull for the developer.
|
usefull for the developer.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user