Compare commits

..

2 Commits

Author SHA1 Message Date
fe351116d6 fix linter error 2025-12-08 21:57:30 +01:00
db35d08385 add template output mode 2025-12-08 21:53:44 +01:00
15 changed files with 97 additions and 159 deletions

View File

@@ -18,9 +18,6 @@ builds:
- windows
- darwin
- freebsd
goarch:
- amd64
- arm64
archives:
- formats: [tar.gz]

32
.woodpecker/image.yaml Normal file
View File

@@ -0,0 +1,32 @@
# https://woodpecker-ci.org/plugins/docker-buildx
# enable Package unit and go to /scip/-/packages after building to link to proj
variables:
- &repo codeberg.org/${CI_REPO_OWNER}/tablizer
steps:
dryrun:
image: docker.io/woodpeckerci/plugin-docker-buildx:latest
settings:
dockerfile: Dockerfile
platforms: linux/amd64
dry_run: true
repo: *repo
tags: latest
when:
event: [pull_request]
publish:
image: docker.io/woodpeckerci/plugin-docker-buildx:latest
settings:
dockerfile: Dockerfile
platforms: linux/amd64
repo: *repo
registry: codeberg.org
tags: latest,${CI_COMMIT_SHA:0:8},${CI_COMMIT_TAG}
username: ${CI_REPO_OWNER}
password:
from_secret: REGISTRY_TOKEN
when:
event: [tag]
branch: main

View File

@@ -45,17 +45,17 @@ Operational Flags:
-n, --numbering Enable header numbering
-N, --no-color Disable pattern highlighting
-H, --no-headers Disable headers display
-s, --separator <string> Custom field separator (maybe char, string or :class:)
-s, --separator <string> Custom field separator
-k, --sort-by <int|name> Sort by column (default: 1)
-z, --fuzzy Use fuzzy search [experimental]
-F, --filter <field[!]=reg> Filter given field with regex, can be used multiple times
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
-R, --regex-transposer </from/to/> Apply /search/replace/ regexp to fields given in -T
-K --regex-colorizer /from/color/ colorize pattern of output (color: fg[:bg])
-j, --json Read JSON input (must be array of hashes)
-I, --interactive Interactively filter and select rows
-g, --auto-headers Generate headers if there are none present in input
-x, --custom-headers a,b,... Use custom headers, separated by comma
--auto-headers Generate headers if there are none present in input
--custom-headers a,b,... Use custom headers, separated by comma
Output Flags (mutually exclusive):
-X, --extended Enable extended output
@@ -63,14 +63,12 @@ Output Flags (mutually exclusive):
-O, --orgtbl Enable org-mode table output
-S, --shell Enable shell evaluable output
-Y, --yaml Enable yaml output
-J, --jsonout Enable JSON output
-C, --csv Enable CSV output
-A, --ascii Default output mode, ascii tabular
-P, --template <tpl> Enable template mode with template <tpl>
-L, --hightlight-lines Use alternating background colors for tables
-o, --ofs <char> Output field separator, used by -A and -C.
-y, --yank-columns Yank specified columns (separated by ,) to clipboard,
space separated
--ofs <char> Output field separator, used by -A and -C.
Sort Mode Flags (mutually exclusive):
-a, --sort-age sort according to age (duration) string
@@ -178,7 +176,7 @@ Here, we modified the 4th column (`-T4`) by replacing every space with
a dash. If you need to work with `/` characters, you can also use any
other separator, for instance: `-R '| |-|'`.
There's also an interactive mode, invoked with the option `-I`, where
There's also an interactive mode, invoked with the option B<-I>, where
you can interactively filter and select rows:
<img width="937" height="293" alt="interactive" src="https://github.com/user-attachments/assets/0d4d65e2-d156-43ed-8021-39047c7939ed" />

View File

@@ -1,5 +1,5 @@
/*
Copyright © 2022-2026 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
@@ -28,7 +28,7 @@ import (
)
const (
Version = "v1.6.0"
Version = "v1.5.12"
MAXPARTS = 2
)
@@ -102,15 +102,15 @@ type Config struct {
SortByColumn string // 1,2
UseSortByColumn []int // []int{1,2}
TransposeColumns string // 1,2
UseTransposeColumns []int // []int{1,2}
Transposers []string // []string{"/ /-/", "/foo/bar/"}
UseTransposers []Transposer // {Search: re, Replace: string}
Colorizers []string // []string{"/ /-/", "/foo/fg[:bg]/"}
UseColorizers []Transposer // {Search: re, Replace: color}
TransposeColumns string // 1,2
UseTransposeColumns []int // []int{1,2}
Transposers []string // []string{"/ /-/", "/foo/bar/"}
UseTransposers []Transposer // {Search: re, Replace: string}
/*
FIXME: make configurable somehow, config file or ENV
see https://github.com/gookit/color.
*/
ColorStyle color.Style
HighlightStyle color.Style
NoHighlightStyle color.Style
@@ -357,23 +357,6 @@ func (conf *Config) PrepareTransposers() error {
return nil
}
func (conf *Config) PrepareColorizers() error {
for _, colorizer := range conf.Colorizers {
parts := strings.Split(colorizer, string(colorizer[0]))
if len(parts) != 4 {
return fmt.Errorf("colorizer function must have the format /regexp/foreground-color[:background-color]/")
}
conf.UseColorizers = append(conf.UseColorizers,
Transposer{
Search: *regexp.MustCompile(parts[1]),
Replace: parts[2]},
)
}
return nil
}
func (conf *Config) CheckEnv() {
// check for environment vars, command line flags have precedence,
// NO_COLOR is being checked by the color module itself.

View File

@@ -39,6 +39,7 @@ func TestPrepareModeFlags(t *testing.T) {
{Modeflag{}, ASCII},
}
// FIXME: use a map for easier printing
for _, testdata := range tests {
testname := fmt.Sprintf("PrepareModeFlags-expect-%d", testdata.expect)
t.Run(testname, func(t *testing.T) {

View File

@@ -1,5 +1,5 @@
/*
Copyright © 2022-2026 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
@@ -95,7 +95,6 @@ func Execute() {
conf.PrepareCustomHeaders(headers)
wrapE(conf.PrepareFilters())
wrapE(conf.PrepareColorizers())
conf.DetermineColormode()
conf.ApplyDefaults()
@@ -192,8 +191,6 @@ func Execute() {
"filter", "F", nil, "Filter by field (field=regexp || field!=regexp)")
rootCmd.PersistentFlags().StringArrayVarP(&conf.Transposers,
"regex-transposer", "R", nil, "apply /search/replace/ regexp to fields given in -T")
rootCmd.PersistentFlags().StringArrayVarP(&conf.Colorizers,
"regex-colorizer", "K", nil, "apply /search/color[:background]/ to the whole output")
// input
rootCmd.PersistentFlags().StringVarP(&conf.InputFile, "read-file", "r", "",

View File

@@ -1,7 +1,7 @@
package cmd
const shortusage = `tablizer [regex,...] [-r file] [flags]
-c col,... show specified columns -L colorize rows
-c col,... show specified columns -L highlight matching lines
-k col,... sort by specified columns -j read JSON input
-F col=reg filter field with regexp -v invert match
-T col,... transpose specified columns -n numberize columns
@@ -13,7 +13,6 @@ const shortusage = `tablizer [regex,...] [-r file] [flags]
-x col,... use custom headers -d debug
-o char use char as output separator -g auto generate headers
-K /pattern/foreground[:background]/ colorize pattern of output
-O org -C CSV -M md -X ext -S shell -Y yaml -J json -P template
-a sort by age -i sort numerically -t sort by time -D sort descending order
-m show manual -v show version --help show detailed help`

View File

@@ -20,7 +20,6 @@ SYNOPSIS
-F, --filter <field[!]=reg> Filter given field with regex, can be used multiple times
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
-R, --regex-transposer </from/to/> Apply /search/replace/ regexp to fields given in -T
-K --regex-colorizer /from/color/ colorize pattern of output (color: fg[:bg])
-j, --json Read JSON input (must be array of hashes)
-I, --interactive Interactively filter and select rows
-g, --auto-headers Generate headers if there are none present in input
@@ -468,20 +467,9 @@ CONFIGURATION AND COLORS
lightGreen, lightMagenta, lightRed, lightWhite, lightYellow, magenta,
red, white, yellow
but you may also use HTML color codes without the hash sign.
The Variables FG and BG are being used to highlight matching rows. The
other *FG and *BG variables are for colored table output (enabled with
the "-L" parameter).
You can also use the option "-K" to colorize particular patterns, not
whole lines. The option can be given multiple times and expects the
following parameter:
-K '/regex/foreground[:background]/
that is, background color is optional. This colorization will applied on
top of any previous colorizations, if any.
The Variables FG and BG are being used to highlight matches. The other
*FG and *BG variables are for colored table output (enabled with the
"-L" parameter).
Colorization can be turned off completely either by setting the
parameter "-N" or the environment variable NO_COLOR to a true value.
@@ -543,7 +531,6 @@ Operational Flags:
-F, --filter <field[!]=reg> Filter given field with regex, can be used multiple times
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
-R, --regex-transposer </from/to/> Apply /search/replace/ regexp to fields given in -T
-K --regex-colorizer /from/color/ colorize pattern of output (color: fg[:bg])
-j, --json Read JSON input (must be array of hashes)
-I, --interactive Interactively filter and select rows
-g, --auto-headers Generate headers if there are none present in input

View File

@@ -1,5 +1,5 @@
/*
Copyright © 2022-2026 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
@@ -22,8 +22,8 @@ import (
"io"
"strings"
"codeberg.org/scip/tablizer/cfg"
"github.com/lithammer/fuzzysearch/fuzzy"
"codeberg.org/scip/tablizer/cfg"
)
/*

View File

@@ -1,5 +1,5 @@
/*
Copyright © 2022-2026 Thomas von Dein
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
@@ -26,8 +26,8 @@ import (
"strconv"
"strings"
"codeberg.org/scip/tablizer/cfg"
"github.com/gookit/color"
"codeberg.org/scip/tablizer/cfg"
)
func findindex(s []int, e int) (int, bool) {
@@ -245,33 +245,35 @@ func reduceColumns(conf cfg.Config, data *Tabdata) {
}
}
// FIXME: refactor this beast!
func colorizeData(conf cfg.Config, output string) string {
if !conf.NoColor && !color.IsConsole(os.Stdout) {
return output
}
switch {
case conf.UseHighlight:
case conf.UseHighlight && color.IsConsole(os.Stdout):
highlight := true
colorized := ""
style := color.Style{}
first := true
for idx, line := range strings.Split(output, "\n") {
if idx == 0 {
style = conf.HighlightHdrStyle
}
for _, line := range strings.Split(output, "\n") {
if highlight {
if first {
// we need to add two spaces to the header line
// because tablewriter omits them for some reason
// in pprint mode. This doesn't matter as long as
// we don't use colorization. But with colors the
// missing spaces can be seen.
if conf.OutputMode == cfg.ASCII {
line += " "
}
switch highlight {
case true:
if idx > 0 {
style = conf.HighlightStyle
line = conf.HighlightHdrStyle.Sprint(line)
first = false
} else {
line = conf.HighlightStyle.Sprint(line)
}
case false:
style = conf.NoHighlightStyle
} else {
line = conf.NoHighlightStyle.Sprint(line)
}
line = style.Sprint(line)
highlight = !highlight
colorized += line + "\n"
@@ -279,7 +281,7 @@ func colorizeData(conf cfg.Config, output string) string {
return colorized
case len(conf.Patterns) > 0:
case len(conf.Patterns) > 0 && !conf.NoColor && color.IsConsole(os.Stdout):
out := output
for _, re := range conf.Patterns {

View File

@@ -1,5 +1,5 @@
/*
Copyright © 2022-2026 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
@@ -23,7 +23,6 @@ import (
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"text/template"
@@ -76,8 +75,8 @@ func printData(writer io.Writer, conf cfg.Config, data *Tabdata) {
}
}
func output(writer io.Writer, conf cfg.Config, str string) {
_, err := fmt.Fprint(writer, colorizeOutput(conf, str))
func output(writer io.Writer, str string) {
_, err := fmt.Fprint(writer, str)
if err != nil {
log.Fatalf("failed to print output: %s", err)
}
@@ -139,7 +138,7 @@ func printOrgmodeData(writer io.Writer, conf cfg.Config, data *Tabdata) {
log.Fatalf("Failed to render table: %s", err)
}
output(writer, conf, color.Sprint(colorizeData(conf, tableString.String())))
output(writer, color.Sprint(colorizeData(conf, tableString.String())))
}
/*
@@ -198,7 +197,7 @@ func printMarkdownData(writer io.Writer, conf cfg.Config, data *Tabdata) {
log.Fatalf("Failed to render table: %s", err)
}
output(writer, conf, color.Sprint(colorizeData(conf, tableString.String())))
output(writer, color.Sprint(colorizeData(conf, tableString.String())))
}
/*
@@ -255,7 +254,7 @@ func printASCIIData(writer io.Writer, conf cfg.Config, data *Tabdata) {
log.Fatalf("Failed to render table: %s", err)
}
output(writer, conf, color.Sprint(colorizeData(conf, tableString.String())))
output(writer, color.Sprint(colorizeData(conf, tableString.String())))
}
/*
@@ -276,7 +275,7 @@ func printExtendedData(writer io.Writer, conf cfg.Config, data *Tabdata) {
}
}
output(writer, conf, colorizeData(conf, out))
output(writer, colorizeData(conf, out))
}
/*
@@ -299,7 +298,7 @@ func printShellData(writer io.Writer, data *Tabdata) {
}
// no colorization here
output(writer, cfg.Config{}, out)
output(writer, out)
}
func printJsonData(writer io.Writer, data *Tabdata) {
@@ -328,7 +327,7 @@ func printJsonData(writer io.Writer, data *Tabdata) {
log.Fatal(err)
}
output(writer, cfg.Config{}, string(jsonstr))
output(writer, string(jsonstr))
}
func printYamlData(writer io.Writer, data *Tabdata) {
@@ -365,7 +364,7 @@ func printYamlData(writer io.Writer, data *Tabdata) {
log.Fatal(err)
}
output(writer, cfg.Config{}, string(yamlstr))
output(writer, string(yamlstr))
}
func printCSVData(writer io.Writer, conf cfg.Config, data *Tabdata) {
@@ -415,23 +414,3 @@ func printTemplateData(writer io.Writer, conf cfg.Config, data *Tabdata) {
log.Fatalf("failed to print output: %s", err)
}
}
func colorizeOutput(conf cfg.Config, input string) string {
if len(conf.UseColorizers) > 0 && !conf.NoColor && color.IsConsole(os.Stdout) {
for _, colorizer := range conf.UseColorizers {
// colorize matching parts of the whole output with given color, if the terminal supports it
// color may contain fg:bg or just fg. Color definitions see https://github.com/gookit/color
input = colorizer.Search.ReplaceAllStringFunc(input, func(in string) string {
col := colorizer.Replace
if strings.Contains(col, ":") {
parts := strings.Split(col, ":")
return color.Sprintf("<fg=%s;bg=%s>%s</>", parts[0], parts[1], in)
}
return color.Sprintf("<fg=%s>%s</>", col, in)
})
}
}
return input
}

View File

@@ -23,8 +23,8 @@ import (
"sort"
"strconv"
"codeberg.org/scip/tablizer/cfg"
"github.com/araddon/dateparse"
"codeberg.org/scip/tablizer/cfg"
)
func sortTable(conf cfg.Config, data *Tabdata) {
@@ -38,12 +38,6 @@ func sortTable(conf cfg.Config, data *Tabdata) {
return
}
for _, column := range conf.UseSortByColumn {
if column > len(data.headers) {
return
}
}
// actual sorting
sort.SliceStable(data.entries, func(i, j int) bool {
// holds the result of a sort of one column

View File

@@ -1,5 +0,0 @@
Date,Account Number,Subject,Amount
20250101,968723487,Dogs Medication Invoice 9919292,-450.00
20250103,172747812,Tax return tax id HHD813D/12564H,+912.14
20250105,987122711,Car repair order 020123,-299.45
20250108,731217273,Rent - 12234 Sunset Blvd,-2960.00
1 Date Account Number Subject Amount
2 20250101 968723487 Dogs Medication Invoice 9919292 -450.00
3 20250103 172747812 Tax return tax id HHD813D/12564H +912.14
4 20250105 987122711 Car repair order 020123 -299.45
5 20250108 731217273 Rent - 12234 Sunset Blvd -2960.00

View File

@@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "TABLIZER 1"
.TH TABLIZER 1 "2026-01-19" "1" "User Commands"
.TH TABLIZER 1 "2025-12-08" "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
@@ -158,7 +158,6 @@ tablizer \- Manipulate tabular output of other programs
\& \-F, \-\-filter <field[!]=reg> Filter given field with regex, can be used multiple times
\& \-T, \-\-transpose\-columns string Transpose the speficied columns (separated by ,)
\& \-R, \-\-regex\-transposer </from/to/> Apply /search/replace/ regexp to fields given in \-T
\& \-K \-\-regex\-colorizer /from/color/ colorize pattern of output (color: fg[:bg])
\& \-j, \-\-json Read JSON input (must be array of hashes)
\& \-I, \-\-interactive Interactively filter and select rows
\& \-g, \-\-auto\-headers Generate headers if there are none present in input
@@ -683,22 +682,9 @@ black, blue, cyan, darkGray, default, green, lightBlue, lightCyan,
lightGreen, lightMagenta, lightRed, lightWhite, lightYellow,
magenta, red, white, yellow
.PP
but you may also use \s-1HTML\s0 color codes without the hash sign.
.PP
The Variables \fB\s-1FG\s0\fR and \fB\s-1BG\s0\fR are being used to highlight matching
rows. The other *FG and *BG variables are for colored table output
(enabled with the \f(CW\*(C`\-L\*(C'\fR parameter).
.PP
You can also use the option \f(CW\*(C`\-K\*(C'\fR to colorize particular patterns, not
whole lines. The option can be given multiple times and expects the
following parameter:
.PP
.Vb 1
\& \-K \*(Aq/regex/foreground[:background]/
.Ve
.PP
that is, background color is optional. This colorization will applied
on top of any previous colorizations, if any.
The Variables \fB\s-1FG\s0\fR and \fB\s-1BG\s0\fR are being used to highlight matches. The
other *FG and *BG variables are for colored table output (enabled with
the \f(CW\*(C`\-L\*(C'\fR parameter).
.PP
Colorization can be turned off completely either by setting the
parameter \f(CW\*(C`\-N\*(C'\fR or the environment variable \fB\s-1NO_COLOR\s0\fR to a true value.

View File

@@ -19,7 +19,6 @@ tablizer - Manipulate tabular output of other programs
-F, --filter <field[!]=reg> Filter given field with regex, can be used multiple times
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
-R, --regex-transposer </from/to/> Apply /search/replace/ regexp to fields given in -T
-K --regex-colorizer /from/color/ colorize pattern of output (color: fg[:bg])
-j, --json Read JSON input (must be array of hashes)
-I, --interactive Interactively filter and select rows
-g, --auto-headers Generate headers if there are none present in input
@@ -515,20 +514,9 @@ black, blue, cyan, darkGray, default, green, lightBlue, lightCyan,
lightGreen, lightMagenta, lightRed, lightWhite, lightYellow,
magenta, red, white, yellow
but you may also use HTML color codes without the hash sign.
The Variables B<FG> and B<BG> are being used to highlight matching
rows. The other *FG and *BG variables are for colored table output
(enabled with the C<-L> parameter).
You can also use the option C<-K> to colorize particular patterns, not
whole lines. The option can be given multiple times and expects the
following parameter:
-K '/regex/foreground[:background]/
that is, background color is optional. This colorization will applied
on top of any previous colorizations, if any.
The Variables B<FG> and B<BG> are being used to highlight matches. The
other *FG and *BG variables are for colored table output (enabled with
the C<-L> parameter).
Colorization can be turned off completely either by setting the
parameter C<-N> or the environment variable B<NO_COLOR> to a true value.