diff --git a/go.mod b/go.mod
index b4330af6..877c34df 100644
--- a/go.mod
+++ b/go.mod
@@ -21,7 +21,7 @@ require (
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
github.com/go-playground/validator/v10 v10.28.0
github.com/hashicorp/nomad/api v0.0.0-20250812204832-62b195aaa535 // v1.10.4
- github.com/jedib0t/go-pretty/v6 v6.6.8
+ github.com/jedib0t/go-pretty/v6 v6.7.7
github.com/matcornic/hermes/v2 v2.1.0
github.com/microcosm-cc/bluemonday v1.0.27
github.com/moby/buildkit v0.25.2
diff --git a/go.sum b/go.sum
index f258db6c..3efb7de6 100644
--- a/go.sum
+++ b/go.sum
@@ -191,8 +191,8 @@ github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 h1:xqgexXAGQgY3HAjNPSaCqn5Aahbo5TKsmhp8VRfr1iQ=
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
-github.com/jedib0t/go-pretty/v6 v6.6.8 h1:JnnzQeRz2bACBobIaa/r+nqjvws4yEhcmaZ4n1QzsEc=
-github.com/jedib0t/go-pretty/v6 v6.6.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
+github.com/jedib0t/go-pretty/v6 v6.7.7 h1:Y1Id3lJ3k4UB8uwWWy3l8EVFnUlx5chR5+VbsofPNX0=
+github.com/jedib0t/go-pretty/v6 v6.7.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/EXAMPLES.md b/vendor/github.com/jedib0t/go-pretty/v6/table/EXAMPLES.md
new file mode 100644
index 00000000..8845187a
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/EXAMPLES.md
@@ -0,0 +1,650 @@
+# Examples
+
+All the examples below are going to start with the following block, although
+nothing except a single Row is mandatory for the `Render()` function to render
+something:
+```golang
+package main
+
+import (
+ "os"
+
+ "github.com/jedib0t/go-pretty/v6/table"
+)
+
+func main() {
+ t := table.NewWriter()
+ t.SetOutputMirror(os.Stdout)
+ t.AppendHeader(table.Row{"#", "First Name", "Last Name", "Salary"})
+ t.AppendRows([]table.Row{
+ {1, "Arya", "Stark", 3000},
+ {20, "Jon", "Snow", 2000, "You know nothing, Jon Snow!"},
+ })
+ t.AppendSeparator()
+ t.AppendRow([]interface{}{300, "Tyrion", "Lannister", 5000})
+ t.AppendFooter(table.Row{"", "", "Total", 10000})
+ t.Render()
+}
+```
+Running the above will result in:
+```
++-----+------------+-----------+--------+-----------------------------+
+| # | FIRST NAME | LAST NAME | SALARY | |
++-----+------------+-----------+--------+-----------------------------+
+| 1 | Arya | Stark | 3000 | |
+| 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
++-----+------------+-----------+--------+-----------------------------+
+| 300 | Tyrion | Lannister | 5000 | |
++-----+------------+-----------+--------+-----------------------------+
+| | | TOTAL | 10000 | |
++-----+------------+-----------+--------+-----------------------------+
+```
+
+---
+
+
+Ready-to-use Styles
+
+Table comes with a bunch of ready-to-use Styles that make the table look really
+good. Set or Change the style using:
+```golang
+ t.SetStyle(table.StyleLight)
+ t.Render()
+```
+to get:
+```
+┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
+│ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+│ 1 │ Arya │ Stark │ 3000 │ │
+│ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+│ 300 │ Tyrion │ Lannister │ 5000 │ │
+├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+│ │ │ TOTAL │ 10000 │ │
+└─────┴────────────┴───────────┴────────┴─────────────────────────────┘
+```
+
+Or if you want to use a full-color mode, and don't care for boxes, use:
+```golang
+ t.SetStyle(table.StyleColoredBright)
+ t.Render()
+```
+to get:
+
+
+
+
+
+---
+
+
+Roll your own Style
+
+You can also roll your own style:
+```golang
+ t.SetStyle(table.Style{
+ Name: "myNewStyle",
+ Box: table.BoxStyle{
+ BottomLeft: "\\",
+ BottomRight: "/",
+ BottomSeparator: "v",
+ Left: "[",
+ LeftSeparator: "{",
+ MiddleHorizontal: "-",
+ MiddleSeparator: "+",
+ MiddleVertical: "|",
+ PaddingLeft: "<",
+ PaddingRight: ">",
+ Right: "]",
+ RightSeparator: "}",
+ TopLeft: "(",
+ TopRight: ")",
+ TopSeparator: "^",
+ UnfinishedRow: " ~~~",
+ },
+ Color: table.ColorOptions{
+ IndexColumn: text.Colors{text.BgCyan, text.FgBlack},
+ Footer: text.Colors{text.BgCyan, text.FgBlack},
+ Header: text.Colors{text.BgHiCyan, text.FgBlack},
+ Row: text.Colors{text.BgHiWhite, text.FgBlack},
+ RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
+ },
+ Format: table.FormatOptions{
+ Footer: text.FormatUpper,
+ Header: text.FormatUpper,
+ Row: text.FormatDefault,
+ },
+ Options: table.Options{
+ DrawBorder: true,
+ SeparateColumns: true,
+ SeparateFooter: true,
+ SeparateHeader: true,
+ SeparateRows: false,
+ },
+ })
+```
+
+Or you can use one of the ready-to-use Styles, and just make a few tweaks:
+```golang
+ t.SetStyle(table.StyleLight)
+ t.Style().Color.Header = text.Colors{text.BgHiCyan, text.FgBlack}
+ t.Style().Color.IndexColumn = text.Colors{text.BgHiCyan, text.FgBlack}
+ t.Style().Format.Footer = text.FormatLower
+ t.Style().Options.DrawBorder = false
+```
+
+
+
+---
+
+
+Customize Horizontal Separators
+
+For more granular control over horizontal lines in your table, you can use `BoxStyleHorizontal` to customize different separator types independently. This allows you to have different horizontal line styles for titles, headers, rows, and footers.
+
+The `BoxStyleHorizontal` struct provides 10 customizable separator types:
+- `TitleTop` / `TitleBottom` - Lines above/below the title
+- `HeaderTop` / `HeaderMiddle` / `HeaderBottom` - Lines in the header section
+- `RowTop` / `RowMiddle` / `RowBottom` - Lines in the data rows section
+- `FooterTop` / `FooterMiddle` / `FooterBottom` - Lines in the footer section
+
+You can customize each separator type using:
+
+```golang
+ tw := table.NewWriter()
+ tw.AppendHeader(table.Row{"#", "First Name", "Last Name", "Salary"})
+ tw.AppendRows([]table.Row{
+ {1, "Arya", "Stark", 3000},
+ {20, "Jon", "Snow", 2000, "You know nothing, Jon Snow!"},
+ {300, "Tyrion", "Lannister", 5000},
+ })
+ tw.AppendFooter(table.Row{"", "", "Total", 10000})
+ tw.SetStyle(table.StyleDefault)
+ tw.Style().Box.Horizontal = &table.BoxStyleHorizontal{
+ HeaderTop: "=", // Thicker line above header
+ HeaderMiddle: "-",
+ HeaderBottom: "~", // Thicker line below header
+ RowTop: "-",
+ RowMiddle: "- ",
+ RowBottom: "-",
+ FooterTop: "~", // Thicker line above footer
+ FooterMiddle: "-",
+ FooterBottom: "=", // Thicker line below footer
+ }
+ tw.Style().Options.SeparateRows = true
+ fmt.Println(tw.Render())
+```
+to get something like:
+```
++=====+============+===========+========+=============================+
+| # | FIRST NAME | LAST NAME | SALARY | |
++~~~~~+~~~~~~~~~~~~+~~~~~~~~~~~+~~~~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
+| 1 | Arya | Stark | 3000 | |
++- - -+- - - - - - +- - - - - -+- - - - +- - - - - - - - - - - - - - -+
+| 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
++- - -+- - - - - - +- - - - - -+- - - - +- - - - - - - - - - - - - - -+
+| 300 | Tyrion | Lannister | 5000 | |
++~~~~~+~~~~~~~~~~~~+~~~~~~~~~~~+~~~~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
+| | | TOTAL | 10000 | |
++=====+============+===========+========+=============================+
+```
+
+When `BoxStyle.Horizontal` is set to a non-nil value, it overrides the `MiddleHorizontal` string for all horizontal separators. If `Horizontal` is nil, the table will automatically use `MiddleHorizontal` for all separator types.
+
+
+
+---
+
+
+Auto-Merge
+
+You can auto-merge cells horizontally and vertically, but you have request for
+it specifically for each row/column using `RowConfig` or `ColumnConfig`.
+
+```golang
+ rowConfigAutoMerge := table.RowConfig{AutoMerge: true}
+
+ t := table.NewWriter()
+ t.AppendHeader(table.Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE"}, rowConfigAutoMerge)
+ t.AppendHeader(table.Row{"Node IP", "Pods", "Namespace", "Container", "EXE", "RUN"})
+ t.AppendRow(table.Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rowConfigAutoMerge)
+ t.AppendRow(table.Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"}, rowConfigAutoMerge)
+ t.AppendRow(table.Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"}, rowConfigAutoMerge)
+ t.AppendRow(table.Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, rowConfigAutoMerge)
+ t.AppendRow(table.Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"}, rowConfigAutoMerge)
+ t.AppendRow(table.Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rowConfigAutoMerge)
+ t.AppendRow(table.Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, rowConfigAutoMerge)
+ t.AppendFooter(table.Row{"", "", "", 7, 5, 3})
+ t.SetAutoIndex(true)
+ t.SetColumnConfigs([]table.ColumnConfig{
+ {Number: 1, AutoMerge: true},
+ {Number: 2, AutoMerge: true},
+ {Number: 3, AutoMerge: true},
+ {Number: 4, AutoMerge: true},
+ {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
+ {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
+ })
+ t.SetStyle(table.StyleLight)
+ t.Style().Options.SeparateRows = true
+ fmt.Println(t.Render())
+```
+to get:
+```
+┌───┬─────────┬────────┬───────────┬───────────┬───────────┐
+│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │
+│ │ │ │ │ ├─────┬─────┤
+│ │ │ │ │ │ EXE │ RUN │
+├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤
+│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │
+├───┤ │ │ ├───────────┼─────┬─────┤
+│ 2 │ │ │ │ C 2 │ Y │ N │
+├───┤ │ ├───────────┼───────────┼─────┴─────┤
+│ 3 │ │ │ NS 1B │ C 3 │ N │
+├───┤ ├────────┼───────────┼───────────┼───────────┤
+│ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │
+├───┤ │ │ ├───────────┼─────┬─────┤
+│ 5 │ │ │ │ C 5 │ Y │ N │
+├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤
+│ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │
+├───┤ │ │ ├───────────┼───────────┤
+│ 7 │ │ │ │ C 7 │ Y │
+├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤
+│ │ │ │ │ 7 │ 5 │ 3 │
+└───┴─────────┴────────┴───────────┴───────────┴─────┴─────┘
+```
+
+
+
+---
+
+
+Paging
+
+You can limit the number of lines rendered in a single "Page". This logic
+can handle rows with multiple lines too. The recommended way is to use the
+`Pager()` interface:
+
+```golang
+ pager := t.Pager(PageSize(1))
+ pager.Render() // Render first page
+ pager.Next() // Move to next page and render
+ pager.Prev() // Move to previous page and render
+ pager.GoTo(3) // Jump to page 3
+ pager.Location() // Get current page number
+```
+
+Or use the deprecated `SetPageSize()` method for simple cases:
+```golang
+ t.SetPageSize(1)
+ t.Render()
+```
+to get:
+```
++-----+------------+-----------+--------+-----------------------------+
+| # | FIRST NAME | LAST NAME | SALARY | |
++-----+------------+-----------+--------+-----------------------------+
+| 1 | Arya | Stark | 3000 | |
++-----+------------+-----------+--------+-----------------------------+
+| | | TOTAL | 10000 | |
++-----+------------+-----------+--------+-----------------------------+
+
++-----+------------+-----------+--------+-----------------------------+
+| # | FIRST NAME | LAST NAME | SALARY | |
++-----+------------+-----------+--------+-----------------------------+
+| 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
++-----+------------+-----------+--------+-----------------------------+
+| | | TOTAL | 10000 | |
++-----+------------+-----------+--------+-----------------------------+
+
++-----+------------+-----------+--------+-----------------------------+
+| # | FIRST NAME | LAST NAME | SALARY | |
++-----+------------+-----------+--------+-----------------------------+
+| 300 | Tyrion | Lannister | 5000 | |
++-----+------------+-----------+--------+-----------------------------+
+| | | TOTAL | 10000 | |
++-----+------------+-----------+--------+-----------------------------+
+```
+
+
+
+---
+
+
+Filtering
+
+Filtering can be done on one or more columns. All filters are applied with AND logic (all must match).
+Filters are applied before sorting.
+
+```golang
+ t.FilterBy([]table.FilterBy{
+ {Name: "Salary", Operator: table.GreaterThan, Value: 2000},
+ {Name: "First Name", Operator: table.Contains, Value: "on"},
+ })
+```
+
+The `Operator` field in `FilterBy` supports various filtering operators:
+- `Equal` / `NotEqual` - Exact match
+- `GreaterThan` / `GreaterThanOrEqual` - Numeric comparisons
+- `LessThan` / `LessThanOrEqual` - Numeric comparisons
+- `Contains` / `NotContains` - String search
+- `StartsWith` / `EndsWith` - String prefix/suffix matching
+- `RegexMatch` / `RegexNotMatch` - Regular expression matching
+
+You can make string comparisons case-insensitive by setting `IgnoreCase: true`:
+```golang
+ t.FilterBy([]table.FilterBy{
+ {Name: "First Name", Operator: table.Equal, Value: "JON", IgnoreCase: true},
+ })
+```
+
+For advanced filtering requirements, you can provide a custom filter function:
+```golang
+ t.FilterBy([]table.FilterBy{
+ {
+ Number: 2,
+ CustomFilter: func(cellValue string) bool {
+ // Custom logic: include rows where first name length > 3
+ return len(cellValue) > 3
+ },
+ },
+ })
+```
+
+Example: Filter by salary and name
+```golang
+ t := table.NewWriter()
+ t.AppendHeader(table.Row{"#", "First Name", "Last Name", "Salary"})
+ t.AppendRows([]table.Row{
+ {1, "Arya", "Stark", 3000},
+ {20, "Jon", "Snow", 2000},
+ {300, "Tyrion", "Lannister", 5000},
+ {400, "Sansa", "Stark", 2500},
+ })
+ t.FilterBy([]table.FilterBy{
+ {Number: 4, Operator: table.GreaterThan, Value: 2000},
+ {Number: 3, Operator: table.Contains, Value: "Stark"},
+ })
+ t.Render()
+```
+to get:
+```
++-----+------------+-----------+--------+
+| # | FIRST NAME | LAST NAME | SALARY |
++-----+------------+-----------+--------+
+| 1 | Arya | Stark | 3000 |
+| 400 | Sansa | Stark | 2500 |
++-----+------------+-----------+--------+
+```
+
+
+
+---
+
+
+Sorting
+
+Sorting can be done on one or more columns. The following code will make the
+rows be sorted first by "First Name" and then by "Last Name" (in case of similar
+"First Name" entries).
+```golang
+ t.SortBy([]table.SortBy{
+ {Name: "First Name", Mode: table.Asc},
+ {Name: "Last Name", Mode: table.Asc},
+ })
+```
+
+The `Mode` field in `SortBy` supports various sorting modes:
+- `Asc` / `Dsc` - Alphabetical ascending/descending
+- `AscNumeric` / `DscNumeric` - Numerical ascending/descending
+- `AscAlphaNumeric` / `DscAlphaNumeric` - Alphabetical first, then numerical
+- `AscNumericAlpha` / `DscNumericAlpha` - Numerical first, then alphabetical
+
+You can also make sorting case-insensitive by setting `IgnoreCase: true`:
+```golang
+ t.SortBy([]table.SortBy{
+ {Name: "First Name", Mode: table.Asc, IgnoreCase: true},
+ })
+```
+
+
+
+---
+
+
+Sorting Customization
+
+For advanced sorting requirements, you can provide a custom comparison function
+using `CustomLess`. This function overrides the `Mode` and `IgnoreCase` settings
+and gives you full control over the sorting logic.
+
+The `CustomLess` function receives two string values (the cell contents converted
+to strings) and must return:
+- `-1` when the first value should come before the second
+- `0` when the values are considered equal (sorting continues to the next column)
+- `1` when the first value should come after the second
+
+Example: Custom numeric sorting that handles string numbers correctly.
+```golang
+ t.SortBy([]table.SortBy{
+ {
+ Number: 1,
+ CustomLess: func(iStr string, jStr string) int {
+ iNum, iErr := strconv.Atoi(iStr)
+ jNum, jErr := strconv.Atoi(jStr)
+ if iErr != nil || jErr != nil {
+ // Fallback to string comparison if not numeric
+ if iStr < jStr {
+ return -1
+ }
+ if iStr > jStr {
+ return 1
+ }
+ return 0
+ }
+ if iNum < jNum {
+ return -1
+ }
+ if iNum > jNum {
+ return 1
+ }
+ return 0
+ },
+ },
+ })
+```
+
+Example: Combining custom sorting with default sorting modes.
+```golang
+ t.SortBy([]table.SortBy{
+ {
+ Number: 1,
+ CustomLess: func(iStr string, jStr string) int {
+ // Custom logic: "same" values come first
+ if iStr == "same" && jStr != "same" {
+ return -1
+ }
+ if iStr != "same" && jStr == "same" {
+ return 1
+ }
+ return 0 // Equal, continue to next column
+ },
+ },
+ {Number: 2, Mode: table.Asc}, // Default alphabetical sort
+ {Number: 3, Mode: table.AscNumeric}, // Default numeric sort
+ })
+```
+
+
+---
+
+
+Wrapping (or) Row/Column Width restrictions
+
+You can restrict the maximum (text) width for a Row:
+```golang
+ t.SetAllowedRowLength(50)
+ t.Render()
+```
+to get:
+```
++-----+------------+-----------+--------+------- ~
+| # | FIRST NAME | LAST NAME | SALARY | ~
++-----+------------+-----------+--------+------- ~
+| 1 | Arya | Stark | 3000 | ~
+| 20 | Jon | Snow | 2000 | You kn ~
++-----+------------+-----------+--------+------- ~
+| 300 | Tyrion | Lannister | 5000 | ~
++-----+------------+-----------+--------+------- ~
+| | | TOTAL | 10000 | ~
++-----+------------+-----------+--------+------- ~
+```
+
+
+
+---
+
+
+Column Control - Alignment, Colors, Width and more
+
+You can control a lot of things about individual cells/columns which overrides
+global properties/styles using the `SetColumnConfig()` interface:
+- Alignment (horizontal & vertical)
+- Colorization
+- Transform individual cells based on the content
+- Visibility
+- Width (minimum & maximum)
+
+```golang
+ nameTransformer := text.Transformer(func(val interface{}) string {
+ return text.Bold.Sprint(val)
+ })
+
+ t.SetColumnConfigs([]ColumnConfig{
+ {
+ Name: "First Name",
+ Align: text.AlignLeft,
+ AlignFooter: text.AlignLeft,
+ AlignHeader: text.AlignLeft,
+ Colors: text.Colors{text.BgBlack, text.FgRed},
+ ColorsHeader: text.Colors{text.BgRed, text.FgBlack, text.Bold},
+ ColorsFooter: text.Colors{text.BgRed, text.FgBlack},
+ Hidden: false,
+ Transformer: nameTransformer,
+ TransformerFooter: nameTransformer,
+ TransformerHeader: nameTransformer,
+ VAlign: text.VAlignMiddle,
+ VAlignFooter: text.VAlignTop,
+ VAlignHeader: text.VAlignBottom,
+ WidthMin: 6,
+ WidthMax: 64,
+ }
+ })
+```
+
+
+
+---
+
+
+CSV
+
+```golang
+ t.RenderCSV()
+```
+to get:
+```
+,First Name,Last Name,Salary,
+1,Arya,Stark,3000,
+20,Jon,Snow,2000,"You know nothing\, Jon Snow!"
+300,Tyrion,Lannister,5000,
+,,Total,10000,
+```
+
+
+
+---
+
+
+HTML Table
+
+```golang
+ t.Style().HTML = table.HTMLOptions{
+ CSSClass: "game-of-thrones",
+ EmptyColumn: " ",
+ EscapeText: true,
+ Newline: "
",
+ ConvertColorsToSpans: true, // Convert ANSI escape sequences to HTML tags with CSS classes
+ }
+ t.RenderHTML()
+```
+to get:
+```html
+
+
+
+ | # |
+ First Name |
+ Last Name |
+ Salary |
+ |
+
+
+
+
+ | 1 |
+ Arya |
+ Stark |
+ 3000 |
+ |
+
+
+ | 20 |
+ Jon |
+ Snow |
+ 2000 |
+ You know nothing, Jon Snow! |
+
+
+ | 300 |
+ Tyrion |
+ Lannister |
+ 5000 |
+ |
+
+
+
+
+ | |
+ |
+ Total |
+ 10000 |
+ |
+
+
+
+```
+
+
+
+---
+
+
+Markdown Table
+
+```golang
+ t.RenderMarkdown()
+```
+to get:
+```markdown
+| # | First Name | Last Name | Salary | |
+| ---:| --- | --- | ---:| --- |
+| 1 | Arya | Stark | 3000 | |
+| 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
+| 300 | Tyrion | Lannister | 5000 | |
+| | | Total | 10000 | |
+```
+
+
+
+---
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/README.md b/vendor/github.com/jedib0t/go-pretty/v6/table/README.md
index 3375bd3c..778041d2 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/table/README.md
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/README.md
@@ -3,42 +3,7 @@
Pretty-print tables into ASCII/Unicode strings.
- - Add Rows one-by-one or as a group (`AppendRow`/`AppendRows`)
- - Add Header(s) and Footer(s) (`AppendHeader`/`AppendFooter`)
- - Add a Separator manually after any Row (`AppendSeparator`)
- - Auto Index Rows (1, 2, 3 ...) and Columns (A, B, C, ...) (`SetAutoIndex`)
- - Auto Merge (_not supported in CSV/Markdown/TSV modes_)
- - Cells in a Row (`RowConfig.AutoMerge`)
- - Columns (`ColumnConfig.AutoMerge`) (_not supported in HTML mode_)
- - Limit the length of
- - Rows (`SetAllowedRowLength`)
- - Columns (`ColumnConfig.Width*`)
- - Auto-size Rows (`Style().Size.WidthMin` and `Style().Size.WidthMax`)
- - Page results by a specified number of Lines (`SetPageSize`)
- - Alignment - Horizontal & Vertical
- - Auto (horizontal) Align (numeric columns aligned Right)
- - Custom (horizontal) Align per column (`ColumnConfig.Align*`)
- - Custom (vertical) VAlign per column with multi-line cell support (`ColumnConfig.VAlign*`)
- - Mirror output to an `io.Writer` (ex. `os.StdOut`) (`SetOutputMirror`)
- - Sort by one or more Columns (`SortBy`)
- - Suppress/hide columns with no content (`SuppressEmptyColumns`)
- - Suppress trailing spaces in the last column (`SupressTrailingSpaces`)
- - Customizable Cell rendering per Column (`ColumnConfig.Transformer*`)
- - Hide any columns that you don't want displayed (`ColumnConfig.Hidden`)
- - Reset Headers/Rows/Footers at will to reuse the same Table Writer (`Reset*`)
- - Completely customizable styles (`SetStyle`/`Style`)
- - Many ready-to-use styles: [style.go](style.go)
- - Colorize Headers/Body/Footers using [../text/color.go](../text/color.go)
- - Custom text-case for Headers/Body/Footers
- - Enable separators between each row
- - Render table without a Border
- - and a lot more...
- - Render as:
- - (ASCII/Unicode) Table
- - CSV
- - HTML Table (with custom CSS Class)
- - Markdown Table
- - TSV
+## Sample Table Rendering
```
+---------------------------------------------------------------------+
@@ -57,396 +22,120 @@ Pretty-print tables into ASCII/Unicode strings.
A demonstration of all the capabilities can be found here:
[../cmd/demo-table](../cmd/demo-table)
-If you want very specific examples, read ahead.
+If you want very specific examples, look at the [EXAMPLES.md](EXAMPLES.md) file.
-**Hint**: I've tried to ensure that almost all supported use-cases are covered
-by unit-tests and that they print the table rendered. Run
-`go test -v github.com/jedib0t/go-pretty/v6/table` to see the test outputs and
-help you figure out how to do something.
+## Features
-# Examples
+### Core Table Building
-All the examples below are going to start with the following block, although
-nothing except a single Row is mandatory for the `Render()` function to render
-something:
-```golang
-package main
+ - Add Rows one-by-one or as a group (`AppendRow`/`AppendRows`)
+ - Add Header(s) and Footer(s) (`AppendHeader`/`AppendFooter`)
+ - Add a Separator manually after any Row (`AppendSeparator`)
+ - Add Title above the table (`SetTitle`)
+ - Add Caption below the table (`SetCaption`)
+ - Import 1D or 2D arrays/grids as rows (`ImportGrid`)
+ - Reset Headers/Rows/Footers at will to reuse the same Table Writer (`Reset*`)
-import (
- "os"
+### Indexing & Navigation
- "github.com/jedib0t/go-pretty/v6/table"
-)
+ - Auto Index Rows (1, 2, 3 ...) and Columns (A, B, C, ...) (`SetAutoIndex`)
+ - Set which column is the index column (`SetIndexColumn`)
+ - Pager interface for navigating through paged output (`Pager()`)
+ - `GoTo(pageNum)` - Jump to specific page
+ - `Next()` - Move to next page
+ - `Prev()` - Move to previous page
+ - `Location()` - Get current page number
+ - `Render()` - Render current page
+ - `SetOutputMirror()` - Mirror output to io.Writer
-func main() {
- t := table.NewWriter()
- t.SetOutputMirror(os.Stdout)
- t.AppendHeader(table.Row{"#", "First Name", "Last Name", "Salary"})
- t.AppendRows([]table.Row{
- {1, "Arya", "Stark", 3000},
- {20, "Jon", "Snow", 2000, "You know nothing, Jon Snow!"},
- })
- t.AppendSeparator()
- t.AppendRow([]interface{}{300, "Tyrion", "Lannister", 5000})
- t.AppendFooter(table.Row{"", "", "Total", 10000})
- t.Render()
-}
-```
-Running the above will result in:
-```
-+-----+------------+-----------+--------+-----------------------------+
-| # | FIRST NAME | LAST NAME | SALARY | |
-+-----+------------+-----------+--------+-----------------------------+
-| 1 | Arya | Stark | 3000 | |
-| 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
-+-----+------------+-----------+--------+-----------------------------+
-| 300 | Tyrion | Lannister | 5000 | |
-+-----+------------+-----------+--------+-----------------------------+
-| | | TOTAL | 10000 | |
-+-----+------------+-----------+--------+-----------------------------+
-```
+### Auto Merge
-## Styles
+ - Auto Merge cells (_not supported in CSV/Markdown/TSV modes_)
+ - Cells in a Row (`RowConfig.AutoMerge`)
+ - Columns (`ColumnConfig.AutoMerge`) (_not supported in HTML mode_)
+ - Custom alignment for merged cells (`RowConfig.AutoMergeAlign`)
-You can customize almost every single thing about the table above. The previous
-example just defaulted to `StyleDefault` during `Render()`. You can use a
-ready-to-use style (as in [style.go](style.go)) or customize it as you want.
+### Size & Width Control
-### Ready-to-use Styles
+ - Limit the length of Rows (`SetAllowedRowLength` or `Style().Size.WidthMax`)
+ - Auto-size Rows (`Style().Size.WidthMin` and `Style().Size.WidthMax`)
+ - Column width control (`ColumnConfig.WidthMin` and `ColumnConfig.WidthMax`)
+ - Custom width enforcement functions (`ColumnConfig.WidthMaxEnforcer`)
+ - Default: `text.WrapText`
+ - Options: `text.WrapSoft`, `text.WrapHard`, `text.Trim`, or custom function
-Table comes with a bunch of ready-to-use Styles that make the table look really
-good. Set or Change the style using:
-```golang
- t.SetStyle(table.StyleLight)
- t.Render()
-```
-to get:
-```
-┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
-│ # │ FIRST NAME │ LAST NAME │ SALARY │ │
-├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
-│ 1 │ Arya │ Stark │ 3000 │ │
-│ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
-├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
-│ 300 │ Tyrion │ Lannister │ 5000 │ │
-├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
-│ │ │ TOTAL │ 10000 │ │
-└─────┴────────────┴───────────┴────────┴─────────────────────────────┘
-```
+### Alignment
-Or if you want to use a full-color mode, and don't care for boxes, use:
-```golang
- t.SetStyle(table.StyleColoredBright)
- t.Render()
-```
-to get:
+ - **Horizontal Alignment**
+ - Auto (numeric columns aligned Right, text aligned Left)
+ - Custom per column (`ColumnConfig.Align`, `AlignHeader`, `AlignFooter`)
+ - Options: Left, Center, Right, Justify, Auto
+ - **Vertical Alignment**
+ - Custom per column with multi-line cell support (`ColumnConfig.VAlign`, `VAlignHeader`, `VAlignFooter`)
+ - Options: Top, Middle, Bottom
-
+### Sorting & Filtering
-### Roll your own Style
+ - **Sorting**
+ - Sort by one or more Columns (`SortBy`)
+ - Multiple column sorting support
+ - Various sort modes: Alphabetical, Numeric, Alpha-numeric, Numeric-alpha
+ - Case-insensitive sorting option (`IgnoreCase`)
+ - Custom sorting functions (`CustomLess`) for advanced sorting logic
+ - **Filtering**
+ - Filter by one or more Columns (`FilterBy`)
+ - Multiple filters with AND logic (all must match)
+ - Various filter operators:
+ - Equality: Equal, NotEqual
+ - Numeric: GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual
+ - String: Contains, NotContains, StartsWith, EndsWith
+ - Regex: RegexMatch, RegexNotMatch
+ - Case-insensitive filtering option (`IgnoreCase`)
+ - Custom filter functions (`CustomFilter`) for advanced filtering logic
+ - Filters are applied before sorting
+ - Suppress/hide columns with no content (`SuppressEmptyColumns`)
+ - Hide specific columns (`ColumnConfig.Hidden`)
+ - Suppress trailing spaces in the last column (`SuppressTrailingSpaces`)
-You can also roll your own style:
-```golang
- t.SetStyle(table.Style{
- Name: "myNewStyle",
- Box: table.BoxStyle{
- BottomLeft: "\\",
- BottomRight: "/",
- BottomSeparator: "v",
- Left: "[",
- LeftSeparator: "{",
- MiddleHorizontal: "-",
- MiddleSeparator: "+",
- MiddleVertical: "|",
- PaddingLeft: "<",
- PaddingRight: ">",
- Right: "]",
- RightSeparator: "}",
- TopLeft: "(",
- TopRight: ")",
- TopSeparator: "^",
- UnfinishedRow: " ~~~",
- },
- Color: table.ColorOptions{
- IndexColumn: text.Colors{text.BgCyan, text.FgBlack},
- Footer: text.Colors{text.BgCyan, text.FgBlack},
- Header: text.Colors{text.BgHiCyan, text.FgBlack},
- Row: text.Colors{text.BgHiWhite, text.FgBlack},
- RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
- },
- Format: table.FormatOptions{
- Footer: text.FormatUpper,
- Header: text.FormatUpper,
- Row: text.FormatDefault,
- },
- Options: table.Options{
- DrawBorder: true,
- SeparateColumns: true,
- SeparateFooter: true,
- SeparateHeader: true,
- SeparateRows: false,
- },
- })
-```
+### Customization & Styling
-Or you can use one of the ready-to-use Styles, and just make a few tweaks:
-```golang
- t.SetStyle(table.StyleLight)
- t.Style().Color.Header = text.Colors{text.BgHiCyan, text.FgBlack}
- t.Style().Color.IndexColumn = text.Colors{text.BgHiCyan, text.FgBlack}
- t.Style().Format.Footer = text.FormatLower
- t.Style().Options.DrawBorder = false
-```
+ - **Row Coloring**
+ - Custom row painter function (`SetRowPainter`)
+ - Row painter with attributes (`RowPainterWithAttributes`)
+ - Access to row number and sorted position
+ - **Cell Transformation**
+ - Customizable Cell rendering per Column (`ColumnConfig.Transformer`, `TransformerHeader`, `TransformerFooter`)
+ - Use built-in transformers from `text` package (Number, JSON, Time, URL, etc.)
+ - **Column Styling**
+ - Per-column colors (`ColumnConfig.Colors`, `ColorsHeader`, `ColorsFooter`)
+ - Per-column alignment (horizontal and vertical)
+ - Per-column width constraints
+ - **Completely customizable styles** (`SetStyle`/`Style`)
+ - Many ready-to-use styles: [style.go](style.go)
+ - `StyleDefault` - Classic ASCII borders
+ - `StyleLight` - Light box-drawing characters
+ - `StyleBold` - Bold box-drawing characters
+ - `StyleDouble` - Double box-drawing characters
+ - `StyleRounded` - Rounded box-drawing characters
+ - `StyleColoredBright` - Bright colors, no borders
+ - `StyleColoredDark` - Dark colors, no borders
+ - Many more colored variants (Blue, Cyan, Green, Magenta, Red, Yellow)
+ - Colorize Headers/Body/Footers using [../text/color.go](../text/color.go)
+ - Custom text-case for Headers/Body/Footers
+ - Enable/disable separators between rows
+ - Render table with or without borders
+ - Customize box-drawing characters
+ - Horizontal separators per section (title, header, rows, footer) using `BoxStyleHorizontal`
+ - Title and caption styling options
+ - HTML rendering options (CSS class, escaping, newlines, color conversion)
+ - Bidirectional text support (`Style().Format.Direction`)
-## Auto-Merge
+### Output Formats
-You can auto-merge cells horizontally and vertically, but you have request for
-it specifically for each row/column using `RowConfig` or `ColumnConfig`.
-
-```golang
- rowConfigAutoMerge := table.RowConfig{AutoMerge: true}
-
- t := table.NewWriter()
- t.AppendHeader(table.Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE"}, rowConfigAutoMerge)
- t.AppendHeader(table.Row{"Node IP", "Pods", "Namespace", "Container", "EXE", "RUN"})
- t.AppendRow(table.Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rowConfigAutoMerge)
- t.AppendRow(table.Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"}, rowConfigAutoMerge)
- t.AppendRow(table.Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"}, rowConfigAutoMerge)
- t.AppendRow(table.Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, rowConfigAutoMerge)
- t.AppendRow(table.Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"}, rowConfigAutoMerge)
- t.AppendRow(table.Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rowConfigAutoMerge)
- t.AppendRow(table.Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, rowConfigAutoMerge)
- t.AppendFooter(table.Row{"", "", "", 7, 5, 3})
- t.SetAutoIndex(true)
- t.SetColumnConfigs([]table.ColumnConfig{
- {Number: 1, AutoMerge: true},
- {Number: 2, AutoMerge: true},
- {Number: 3, AutoMerge: true},
- {Number: 4, AutoMerge: true},
- {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
- {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
- })
- t.SetStyle(table.StyleLight)
- t.Style().Options.SeparateRows = true
- fmt.Println(t.Render())
-```
-to get:
-```
-┌───┬─────────┬────────┬───────────┬───────────┬───────────┐
-│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │
-│ │ │ │ │ ├─────┬─────┤
-│ │ │ │ │ │ EXE │ RUN │
-├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤
-│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │
-├───┤ │ │ ├───────────┼─────┬─────┤
-│ 2 │ │ │ │ C 2 │ Y │ N │
-├───┤ │ ├───────────┼───────────┼─────┴─────┤
-│ 3 │ │ │ NS 1B │ C 3 │ N │
-├───┤ ├────────┼───────────┼───────────┼───────────┤
-│ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │
-├───┤ │ │ ├───────────┼─────┬─────┤
-│ 5 │ │ │ │ C 5 │ Y │ N │
-├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤
-│ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │
-├───┤ │ │ ├───────────┼───────────┤
-│ 7 │ │ │ │ C 7 │ Y │
-├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤
-│ │ │ │ │ 7 │ 5 │ 3 │
-└───┴─────────┴────────┴───────────┴───────────┴─────┴─────┘
-```
-
-## Paging
-
-You can limit then number of lines rendered in a single "Page". This logic
-can handle rows with multiple lines too. Here is a simple example:
-```golang
- t.SetPageSize(1)
- t.Render()
-```
-to get:
-```
-+-----+------------+-----------+--------+-----------------------------+
-| # | FIRST NAME | LAST NAME | SALARY | |
-+-----+------------+-----------+--------+-----------------------------+
-| 1 | Arya | Stark | 3000 | |
-+-----+------------+-----------+--------+-----------------------------+
-| | | TOTAL | 10000 | |
-+-----+------------+-----------+--------+-----------------------------+
-
-+-----+------------+-----------+--------+-----------------------------+
-| # | FIRST NAME | LAST NAME | SALARY | |
-+-----+------------+-----------+--------+-----------------------------+
-| 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
-+-----+------------+-----------+--------+-----------------------------+
-| | | TOTAL | 10000 | |
-+-----+------------+-----------+--------+-----------------------------+
-
-+-----+------------+-----------+--------+-----------------------------+
-| # | FIRST NAME | LAST NAME | SALARY | |
-+-----+------------+-----------+--------+-----------------------------+
-| 300 | Tyrion | Lannister | 5000 | |
-+-----+------------+-----------+--------+-----------------------------+
-| | | TOTAL | 10000 | |
-+-----+------------+-----------+--------+-----------------------------+
-```
-
-## Sorting
-
-Sorting can be done on one or more columns. The following code will make the
-rows be sorted first by "First Name" and then by "Last Name" (in case of similar
-"First Name" entries).
-```golang
- t.SortBy([]table.SortBy{
- {Name: "First Name", Mode: table.Asc},
- {Name: "Last Name", Mode: table.Asc},
- })
-```
-
-## Wrapping (or) Row/Column Width restrictions
-
-You can restrict the maximum (text) width for a Row:
-```golang
- t.SetAllowedRowLength(50)
- t.Render()
-```
-to get:
-```
-+-----+------------+-----------+--------+------- ~
-| # | FIRST NAME | LAST NAME | SALARY | ~
-+-----+------------+-----------+--------+------- ~
-| 1 | Arya | Stark | 3000 | ~
-| 20 | Jon | Snow | 2000 | You kn ~
-+-----+------------+-----------+--------+------- ~
-| 300 | Tyrion | Lannister | 5000 | ~
-+-----+------------+-----------+--------+------- ~
-| | | TOTAL | 10000 | ~
-+-----+------------+-----------+--------+------- ~
-```
-
-## Column Control - Alignment, Colors, Width and more
-
-You can control a lot of things about individual cells/columns which overrides
-global properties/styles using the `SetColumnConfig()` interface:
-- Alignment (horizontal & vertical)
-- Colorization
-- Transform individual cells based on the content
-- Visibility
-- Width (minimum & maximum)
-
-```golang
- nameTransformer := text.Transformer(func(val interface{}) string {
- return text.Bold.Sprint(val)
- })
-
- t.SetColumnConfigs([]ColumnConfig{
- {
- Name: "First Name",
- Align: text.AlignLeft,
- AlignFooter: text.AlignLeft,
- AlignHeader: text.AlignLeft,
- Colors: text.Colors{text.BgBlack, text.FgRed},
- ColorsHeader: text.Colors{text.BgRed, text.FgBlack, text.Bold},
- ColorsFooter: text.Colors{text.BgRed, text.FgBlack},
- Hidden: false,
- Transformer: nameTransformer,
- TransformerFooter: nameTransformer,
- TransformerHeader: nameTransformer,
- VAlign: text.VAlignMiddle,
- VAlignFooter: text.VAlignTop,
- VAlignHeader: text.VAlignBottom,
- WidthMin: 6,
- WidthMax: 64,
- }
- })
-```
-
-## Render As ...
-
-Tables can be rendered in other common formats such as:
-
-### ... CSV
-
-```golang
- t.RenderCSV()
-```
-to get:
-```
-,First Name,Last Name,Salary,
-1,Arya,Stark,3000,
-20,Jon,Snow,2000,"You know nothing\, Jon Snow!"
-300,Tyrion,Lannister,5000,
-,,Total,10000,
-```
-
-### ... HTML Table
-
-```golang
- t.Style().HTML = table.HTMLOptions{
- CSSClass: "game-of-thrones",
- EmptyColumn: " ",
- EscapeText: true,
- Newline: "
",
- }
- t.RenderHTML()
-```
-to get:
-```html
-
-
-
- | # |
- First Name |
- Last Name |
- Salary |
- |
-
-
-
-
- | 1 |
- Arya |
- Stark |
- 3000 |
- |
-
-
- | 20 |
- Jon |
- Snow |
- 2000 |
- You know nothing, Jon Snow! |
-
-
- | 300 |
- Tyrion |
- Lannister |
- 5000 |
- |
-
-
-
-
- | |
- |
- Total |
- 10000 |
- |
-
-
-
-```
-
-### ... Markdown Table
-
-```golang
- t.RenderMarkdown()
-```
-to get:
-```markdown
-| # | First Name | Last Name | Salary | |
-| ---:| --- | --- | ---:| --- |
-| 1 | Arya | Stark | 3000 | |
-| 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
-| 300 | Tyrion | Lannister | 5000 | |
-| | | Total | 10000 | |
-```
+ - **Render as:**
+ - (ASCII/Unicode) Table - Human-readable pretty format
+ - CSV - Comma-separated values
+ - HTML Table - With custom CSS Class and options
+ - Markdown Table - Markdown-compatible format
+ - TSV - Tab-separated values
+ - Mirror output to an `io.Writer` (ex. `os.StdOut`) (`SetOutputMirror`)
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/filter.go b/vendor/github.com/jedib0t/go-pretty/v6/table/filter.go
new file mode 100644
index 00000000..7462cfc8
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/filter.go
@@ -0,0 +1,250 @@
+package table
+
+import (
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// FilterBy defines what to filter (Column Name or Number), how to filter (Operator),
+// and the value to compare against.
+type FilterBy struct {
+ // Name is the name of the Column as it appears in the first Header row.
+ // If a Header is not provided, or the name is not found in the header, this
+ // will not work.
+ Name string
+ // Number is the Column # from left. When specified, it overrides the Name
+ // property. If you know the exact Column number, use this instead of Name.
+ Number int
+
+ // Operator defines how to compare the column value against the Value.
+ Operator FilterOperator
+
+ // Value is the value to compare against. The type should match the expected
+ // comparison type (string for string operations, numeric for numeric operations).
+ // For Contains, StartsWith, EndsWith, and RegexMatch, Value should be a string.
+ // For numeric comparisons (Equal, NotEqual, GreaterThan, etc.), Value can be
+ // a number (int, float64) or a string representation of a number.
+ Value interface{}
+
+ // IgnoreCase makes string comparisons case-insensitive (only applies to
+ // string-based operators).
+ IgnoreCase bool
+
+ // CustomFilter is a function that can be used to filter rows in a custom
+ // manner. Note that:
+ // * This overrides and ignores the Operator, Value, and IgnoreCase settings
+ // * This is called after the column contents are converted to string form
+ // * This function is expected to return:
+ // * true => include the row
+ // * false => exclude the row
+ //
+ // Use this when the default filtering logic is not sufficient.
+ CustomFilter func(cellValue string) bool
+}
+
+// FilterOperator defines how to filter.
+type FilterOperator int
+
+const (
+ // Equal filters rows where the column value equals the Value.
+ Equal FilterOperator = iota
+ // NotEqual filters rows where the column value does not equal the Value.
+ NotEqual
+ // GreaterThan filters rows where the column value is greater than the Value.
+ GreaterThan
+ // GreaterThanOrEqual filters rows where the column value is greater than or equal to the Value.
+ GreaterThanOrEqual
+ // LessThan filters rows where the column value is less than the Value.
+ LessThan
+ // LessThanOrEqual filters rows where the column value is less than or equal to the Value.
+ LessThanOrEqual
+ // Contains filters rows where the column value contains the Value (string search).
+ Contains
+ // NotContains filters rows where the column value does not contain the Value (string search).
+ NotContains
+ // StartsWith filters rows where the column value starts with the Value.
+ StartsWith
+ // EndsWith filters rows where the column value ends with the Value.
+ EndsWith
+ // RegexMatch filters rows where the column value matches the Value as a regular expression.
+ RegexMatch
+ // RegexNotMatch filters rows where the column value does not match the Value as a regular expression.
+ RegexNotMatch
+)
+
+func (t *Table) parseFilterBy(filterBy []FilterBy) []FilterBy {
+ var resFilterBy []FilterBy
+ for _, filter := range filterBy {
+ colNum := 0
+ if filter.Number > 0 && filter.Number <= t.numColumns {
+ colNum = filter.Number
+ } else if filter.Name != "" && len(t.rowsHeaderRaw) > 0 {
+ // Parse from raw header rows
+ for idx, colName := range t.rowsHeaderRaw[0] {
+ if fmt.Sprint(colName) == filter.Name {
+ colNum = idx + 1
+ break
+ }
+ }
+ }
+ if colNum > 0 {
+ resFilterBy = append(resFilterBy, FilterBy{
+ Name: filter.Name,
+ Number: colNum,
+ Operator: filter.Operator,
+ Value: filter.Value,
+ IgnoreCase: filter.IgnoreCase,
+ CustomFilter: filter.CustomFilter,
+ })
+ }
+ }
+ return resFilterBy
+}
+
+func (t *Table) matchesFiltersRaw(row Row, filters []FilterBy) bool {
+ // All filters must match (AND logic)
+ for _, filter := range filters {
+ if !t.matchesFilterRaw(row, filter) {
+ return false
+ }
+ }
+ return true
+}
+
+func (t *Table) matchesFilterRaw(row Row, filter FilterBy) bool {
+ colIdx := filter.Number - 1
+ if colIdx < 0 || colIdx >= len(row) {
+ return false
+ }
+
+ cellValue := row[colIdx]
+ cellValueStr := fmt.Sprint(cellValue)
+
+ // Use custom filter if provided
+ if filter.CustomFilter != nil {
+ return filter.CustomFilter(cellValueStr)
+ }
+
+ // Use operator-based filtering
+ return t.matchesOperator(cellValueStr, filter)
+}
+
+func (t *Table) matchesOperator(cellValue string, filter FilterBy) bool {
+ switch filter.Operator {
+ case Equal:
+ return t.compareEqual(cellValue, filter.Value, filter.IgnoreCase)
+ case NotEqual:
+ return !t.compareEqual(cellValue, filter.Value, filter.IgnoreCase)
+ case GreaterThan:
+ return t.compareNumeric(cellValue, filter.Value, func(a, b float64) bool { return a > b })
+ case GreaterThanOrEqual:
+ return t.compareNumeric(cellValue, filter.Value, func(a, b float64) bool { return a >= b })
+ case LessThan:
+ return t.compareNumeric(cellValue, filter.Value, func(a, b float64) bool { return a < b })
+ case LessThanOrEqual:
+ return t.compareNumeric(cellValue, filter.Value, func(a, b float64) bool { return a <= b })
+ case Contains:
+ return t.compareContains(cellValue, filter.Value, filter.IgnoreCase)
+ case NotContains:
+ return !t.compareContains(cellValue, filter.Value, filter.IgnoreCase)
+ case StartsWith:
+ return t.compareStartsWith(cellValue, filter.Value, filter.IgnoreCase)
+ case EndsWith:
+ return t.compareEndsWith(cellValue, filter.Value, filter.IgnoreCase)
+ case RegexMatch:
+ return t.compareRegexMatch(cellValue, filter.Value, filter.IgnoreCase)
+ case RegexNotMatch:
+ return !t.compareRegexMatch(cellValue, filter.Value, filter.IgnoreCase)
+ default:
+ return false
+ }
+}
+
+func (t *Table) compareEqual(cellValue string, filterValue interface{}, ignoreCase bool) bool {
+ filterStr := fmt.Sprint(filterValue)
+ if ignoreCase {
+ return strings.EqualFold(cellValue, filterStr)
+ }
+ return cellValue == filterStr
+}
+
+func (t *Table) compareNumeric(cellValue string, filterValue interface{}, compareFunc func(float64, float64) bool) bool {
+ cellNum, cellErr := strconv.ParseFloat(cellValue, 64)
+ if cellErr != nil {
+ return false
+ }
+
+ var filterNum float64
+ switch v := filterValue.(type) {
+ case int:
+ filterNum = float64(v)
+ case int64:
+ filterNum = float64(v)
+ case float64:
+ filterNum = v
+ case float32:
+ filterNum = float64(v)
+ case string:
+ var err error
+ filterNum, err = strconv.ParseFloat(v, 64)
+ if err != nil {
+ return false
+ }
+ default:
+ // Try to convert to string and parse
+ filterStr := fmt.Sprint(filterValue)
+ var err error
+ filterNum, err = strconv.ParseFloat(filterStr, 64)
+ if err != nil {
+ return false
+ }
+ }
+
+ return compareFunc(cellNum, filterNum)
+}
+
+func (t *Table) compareContains(cellValue string, filterValue interface{}, ignoreCase bool) bool {
+ filterStr := fmt.Sprint(filterValue)
+ if ignoreCase {
+ return strings.Contains(strings.ToLower(cellValue), strings.ToLower(filterStr))
+ }
+ return strings.Contains(cellValue, filterStr)
+}
+
+func (t *Table) compareStartsWith(cellValue string, filterValue interface{}, ignoreCase bool) bool {
+ filterStr := fmt.Sprint(filterValue)
+ if ignoreCase {
+ return strings.HasPrefix(strings.ToLower(cellValue), strings.ToLower(filterStr))
+ }
+ return strings.HasPrefix(cellValue, filterStr)
+}
+
+func (t *Table) compareEndsWith(cellValue string, filterValue interface{}, ignoreCase bool) bool {
+ filterStr := fmt.Sprint(filterValue)
+ if ignoreCase {
+ return strings.HasSuffix(strings.ToLower(cellValue), strings.ToLower(filterStr))
+ }
+ return strings.HasSuffix(cellValue, filterStr)
+}
+
+func (t *Table) compareRegexMatch(cellValue string, filterValue interface{}, ignoreCase bool) bool {
+ filterStr := fmt.Sprint(filterValue)
+
+ // Compile the regex pattern
+ var pattern *regexp.Regexp
+ var err error
+ if ignoreCase {
+ pattern, err = regexp.Compile("(?i)" + filterStr)
+ } else {
+ pattern, err = regexp.Compile(filterStr)
+ }
+
+ if err != nil {
+ // If regex compilation fails, fall back to simple string matching
+ return t.compareEqual(cellValue, filterValue, ignoreCase)
+ }
+
+ return pattern.MatchString(cellValue)
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/render.go b/vendor/github.com/jedib0t/go-pretty/v6/table/render.go
index 4ef68f99..a1b6cfe9 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/table/render.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/render.go
@@ -20,7 +20,7 @@ import (
// │ │ │ TOTAL │ 10000 │ │
// └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
func (t *Table) Render() string {
- t.initForRender()
+ t.initForRender(renderModeDefault)
var out strings.Builder
if t.numColumns > 0 {
@@ -50,6 +50,7 @@ func (t *Table) Render() string {
return t.render(&out)
}
+//gocyclo:ignore
func (t *Table) renderColumn(out *strings.Builder, row rowStr, colIdx int, maxColumnLength int, hint renderHint) int {
numColumnsRendered := 1
@@ -93,11 +94,10 @@ func (t *Table) renderColumn(out *strings.Builder, row rowStr, colIdx int, maxCo
numColumnsRendered++
}
}
- colStr = align.Apply(colStr, maxColumnLength)
// pad both sides of the column
if !hint.isSeparatorRow || (hint.isSeparatorRow && mergeVertically) {
- colStr = t.style.Box.PaddingLeft + colStr + t.style.Box.PaddingRight
+ colStr = t.style.Box.PaddingLeft + align.Apply(colStr, maxColumnLength) + t.style.Box.PaddingRight
}
t.renderColumnColorized(out, colIdx, colStr, hint)
@@ -112,9 +112,9 @@ func (t *Table) renderColumnAutoIndex(out *strings.Builder, hint renderHint) {
if hint.isSeparatorRow {
numChars := t.autoIndexVIndexMaxLength + utf8.RuneCountInString(t.style.Box.PaddingLeft) +
utf8.RuneCountInString(t.style.Box.PaddingRight)
- chars := t.style.Box.MiddleHorizontal
+ chars := t.style.Box.middleHorizontal(hint.separatorType)
if hint.isAutoIndexColumn && hint.isHeaderOrFooterSeparator() {
- chars = text.RepeatAndTrim(" ", len(t.style.Box.MiddleHorizontal))
+ chars = text.RepeatAndTrim(" ", len(chars))
}
outAutoIndex.WriteString(text.RepeatAndTrim(chars, numChars))
} else {
@@ -239,7 +239,7 @@ func (t *Table) renderLineMergeOutputs(out *strings.Builder, outLine *strings.Bu
}
func (t *Table) renderMarginLeft(out *strings.Builder, hint renderHint) {
- out.WriteString(t.style.Format.Direction.Modifier())
+ out.WriteString(t.directionModifier)
if t.style.Options.DrawBorder {
border := t.getBorderLeft(hint)
colors := t.getBorderColors(hint)
@@ -304,8 +304,10 @@ func (t *Table) renderRowSeparator(out *strings.Builder, hint renderHint) {
} else if hint.isFooterRow && !t.style.Options.SeparateFooter {
return
}
+
hint.isSeparatorRow = true
- t.renderLine(out, t.rowSeparator, hint)
+ separator := t.rowSeparatorStrings[hint.separatorType]
+ t.renderLine(out, t.rowSeparators[separator], hint)
}
func (t *Table) renderRows(out *strings.Builder, rows []rowStr, hint renderHint) {
@@ -316,8 +318,17 @@ func (t *Table) renderRows(out *strings.Builder, rows []rowStr, hint renderHint)
t.renderRow(out, row, hint)
if t.shouldSeparateRows(rowIdx, len(rows)) {
- hint.isFirstRow = false
- t.renderRowSeparator(out, hint)
+ hintSep := hint
+ hintSep.isFirstRow = false
+ hintSep.isSeparatorRow = true
+ if hintSep.isHeaderRow {
+ hintSep.separatorType = separatorTypeHeaderMiddle
+ } else if hintSep.isFooterRow {
+ hintSep.separatorType = separatorTypeFooterMiddle
+ } else {
+ hintSep.separatorType = separatorTypeRowMiddle
+ }
+ t.renderRowSeparator(out, hintSep)
}
}
}
@@ -328,46 +339,69 @@ func (t *Table) renderRowsBorderBottom(out *strings.Builder) {
isBorderBottom: true,
isFooterRow: true,
rowNumber: len(t.rowsFooter),
+ separatorType: separatorTypeFooterBottom,
})
} else {
t.renderRowSeparator(out, renderHint{
isBorderBottom: true,
isFooterRow: false,
rowNumber: len(t.rows),
+ separatorType: separatorTypeRowBottom,
})
}
}
func (t *Table) renderRowsBorderTop(out *strings.Builder) {
+ st := separatorTypeHeaderTop
+ if t.title != "" {
+ st = separatorTypeTitleBottom
+ } else if len(t.rowsHeader) == 0 && !t.autoIndex {
+ st = separatorTypeRowTop
+ }
+
if len(t.rowsHeader) > 0 || t.autoIndex {
t.renderRowSeparator(out, renderHint{
- isBorderTop: true,
- isHeaderRow: true,
- rowNumber: 0,
+ isBorderTop: true,
+ isHeaderRow: true,
+ isSeparatorRow: true,
+ rowNumber: 0,
+ separatorType: st,
})
} else {
t.renderRowSeparator(out, renderHint{
- isBorderTop: true,
- isHeaderRow: false,
- rowNumber: 0,
+ isBorderTop: true,
+ isHeaderRow: false,
+ isSeparatorRow: true,
+ rowNumber: 0,
+ separatorType: st,
})
}
}
func (t *Table) renderRowsFooter(out *strings.Builder) {
if len(t.rowsFooter) > 0 {
- t.renderRowSeparator(out, renderHint{
- isFooterRow: true,
- isFirstRow: true,
- isSeparatorRow: true,
- })
+ // Only add separator before footer if there are data rows.
+ // Otherwise, renderRowsHeader already added one.
+ if len(t.rows) > 0 {
+ t.renderRowSeparator(out, renderHint{
+ isFooterRow: true,
+ isFirstRow: true,
+ isSeparatorRow: true,
+ separatorType: separatorTypeFooterTop,
+ })
+ }
t.renderRows(out, t.rowsFooter, renderHint{isFooterRow: true})
}
}
func (t *Table) renderRowsHeader(out *strings.Builder) {
if len(t.rowsHeader) > 0 || t.autoIndex {
- hintSeparator := renderHint{isHeaderRow: true, isLastRow: true, isSeparatorRow: true}
+ hintSeparator := renderHint{
+ isHeaderRow: true,
+ isLastRow: true,
+ isSeparatorRow: true,
+ separatorType: separatorTypeHeaderMiddle,
+ }
if len(t.rowsHeader) > 0 {
t.renderRows(out, t.rowsHeader, renderHint{isHeaderRow: true})
@@ -376,7 +410,13 @@ func (t *Table) renderRowsHeader(out *strings.Builder) {
t.renderRow(out, t.getAutoIndexColumnIDs(), renderHint{isAutoIndexRow: true, isHeaderRow: true})
hintSeparator.rowNumber = 1
}
- t.renderRowSeparator(out, hintSeparator)
+
+ // Only add separator after header if there are data rows or footer rows.
+ // Otherwise, the bottom border is rendered directly.
+ if len(t.rows) > 0 || len(t.rowsFooter) > 0 || !t.style.Options.DoNotRenderSeparatorWhenEmpty {
+ hintSeparator.separatorType = separatorTypeHeaderBottom
+ t.renderRowSeparator(out, hintSeparator)
+ }
}
}
@@ -393,8 +433,9 @@ func (t *Table) renderTitle(out *strings.Builder) {
}
if t.style.Options.DrawBorder {
lenBorder := rowLength - text.StringWidthWithoutEscSequences(t.style.Box.TopLeft+t.style.Box.TopRight)
+ middleHorizontal := t.style.Box.middleHorizontal(separatorTypeTitleTop)
out.WriteString(colorsBorder.Sprint(t.style.Box.TopLeft))
- out.WriteString(colorsBorder.Sprint(text.RepeatAndTrim(t.style.Box.MiddleHorizontal, lenBorder)))
+ out.WriteString(colorsBorder.Sprint(text.RepeatAndTrim(middleHorizontal, lenBorder)))
out.WriteString(colorsBorder.Sprint(t.style.Box.TopRight))
}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/render_csv.go b/vendor/github.com/jedib0t/go-pretty/v6/table/render_csv.go
index 831194ef..e9ea5ef8 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/table/render_csv.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/render_csv.go
@@ -14,7 +14,7 @@ import (
// 300,Tyrion,Lannister,5000,
// ,,Total,10000,
func (t *Table) RenderCSV() string {
- t.initForRender()
+ t.initForRender(renderModeCSV)
var out strings.Builder
if t.numColumns > 0 {
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/render_hint.go b/vendor/github.com/jedib0t/go-pretty/v6/table/render_hint.go
index e46cdc84..167cf88d 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/table/render_hint.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/render_hint.go
@@ -15,6 +15,7 @@ type renderHint struct {
isTitleRow bool // title row?
rowLineNumber int // the line number for a multi-line row
rowNumber int // the row number/index
+ separatorType separatorType
}
func (h *renderHint) isBorderOrSeparator() bool {
@@ -37,3 +38,13 @@ func (h *renderHint) isHeaderOrFooterSeparator() bool {
func (h *renderHint) isLastLineOfLastRow() bool {
return h.isLastLineOfRow && h.isLastRow
}
+
+type renderMode string
+
+const (
+ renderModeDefault renderMode = "default"
+ renderModeCSV renderMode = "csv"
+ renderModeMarkdown renderMode = "markdown"
+ renderModeTSV renderMode = "tsv"
+ renderModeHTML renderMode = "html"
+)
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/render_html.go b/vendor/github.com/jedib0t/go-pretty/v6/table/render_html.go
index 729f95ee..fec68f00 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/table/render_html.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/render_html.go
@@ -60,7 +60,7 @@ const (
//
//
func (t *Table) RenderHTML() string {
- t.initForRender()
+ t.initForRender(renderModeHTML)
var out strings.Builder
if t.numColumns > 0 {
@@ -106,11 +106,15 @@ func (t *Table) htmlRenderCaption(out *strings.Builder) {
}
func (t *Table) htmlRenderColumn(out *strings.Builder, colStr string) {
- if t.style.HTML.EscapeText {
+ // convertEscSequencesToSpans already escapes text content, so skip
+ // EscapeText if ConvertColorsToSpans is true
+ if t.style.HTML.ConvertColorsToSpans {
+ colStr = convertEscSequencesToSpans(colStr)
+ } else if t.style.HTML.EscapeText {
colStr = html.EscapeString(colStr)
}
if t.style.HTML.Newline != "\n" {
- colStr = strings.Replace(colStr, "\n", t.style.HTML.Newline, -1)
+ colStr = strings.ReplaceAll(colStr, "\n", t.style.HTML.Newline)
}
out.WriteString(colStr)
}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/render_init.go b/vendor/github.com/jedib0t/go-pretty/v6/table/render_init.go
index 333c3988..d79aa0f8 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/table/render_init.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/render_init.go
@@ -43,11 +43,15 @@ func (t *Table) analyzeAndStringifyColumn(colIdx int, col interface{}, hint rend
} else if colStrVal, ok := col.(string); ok {
colStr = colStrVal
} else {
- colStr = fmt.Sprint(col)
+ colStr = convertValueToString(col)
}
colStr = strings.ReplaceAll(colStr, "\t", " ")
colStr = text.ProcessCRLF(colStr)
- return fmt.Sprintf("%s%s", t.style.Format.Direction.Modifier(), colStr)
+ // Avoid fmt.Sprintf when direction modifier is empty (most common case)
+ if t.directionModifier == "" {
+ return colStr
+ }
+ return t.directionModifier + colStr
}
func (t *Table) extractMaxColumnLengths(rows []rowStr, hint renderHint) {
@@ -149,13 +153,18 @@ func (t *Table) reBalanceMaxMergedColumnLengths() {
}
}
-func (t *Table) initForRender() {
+func (t *Table) initForRender(mode renderMode) {
+ t.renderMode = mode
+
// pick a default style if none was set until now
t.Style()
// reset rendering state
t.reset()
+ // cache the direction modifier to avoid repeated calls
+ t.directionModifier = t.style.Format.Direction.Modifier()
+
// initialize the column configs and normalize them
t.initForRenderColumnConfigs()
@@ -280,11 +289,15 @@ func (t *Table) initForRenderPaddedColumns() {
}
func (t *Table) initForRenderRows() {
- // auto-index: calc the index column's max length
- t.autoIndexVIndexMaxLength = len(fmt.Sprint(len(t.rowsRaw)))
+ // filter the rows as requested (before stringification and sorting)
+ t.initForRenderFilterRows()
- // stringify all the rows to make it easy to render
- t.rows = t.initForRenderRowsStringify(t.rowsRaw, renderHint{})
+ // auto-index: calc the index column's max length
+ t.autoIndexVIndexMaxLength = len(fmt.Sprint(len(t.rowsRawFiltered)))
+
+ // stringify the filtered rows
+ t.numColumns = 0
+ t.rows = t.initForRenderRowsStringify(t.rowsRawFiltered, renderHint{})
t.rowsFooter = t.initForRenderRowsStringify(t.rowsFooterRaw, renderHint{isFooterRow: true})
t.rowsHeader = t.initForRenderRowsStringify(t.rowsHeaderRaw, renderHint{isHeaderRow: true})
@@ -301,6 +314,60 @@ func (t *Table) initForRenderRows() {
t.initForRenderHideColumns()
}
+// initForRenderFilterRows filters the raw rows by removing non-matching rows from t.rowsRawFiltered.
+func (t *Table) initForRenderFilterRows() {
+ // Restore original rows before filtering (in case of multiple renders with different filters)
+ if len(t.rowsRaw) > 0 {
+ t.rowsRawFiltered = make([]Row, len(t.rowsRaw))
+ for i, row := range t.rowsRaw {
+ rowCopy := make(Row, len(row))
+ copy(rowCopy, row)
+ t.rowsRawFiltered[i] = rowCopy
+ }
+ }
+
+ if len(t.filterBy) == 0 {
+ // No filters, nothing to do
+ return
+ }
+
+ // Store original separators and track which rows are kept
+ originalSeparators := make(map[int]bool)
+ for k, v := range t.separators {
+ originalSeparators[k] = v
+ }
+
+ // Calculate numColumns from raw rows/headers for filter parsing
+ t.calculateNumColumnsFromRaw()
+ parsedFilterBy := t.parseFilterBy(t.filterBy)
+ if len(parsedFilterBy) == 0 {
+ // No valid filters, nothing to do
+ return
+ }
+
+ // Filter rows in place and track which original rows were kept
+ filteredRows := t.rowsRawFiltered[:0]
+ keptIndices := make([]int, 0, len(t.rowsRawFiltered))
+ for origIdx, row := range t.rowsRawFiltered {
+ if t.matchesFiltersRaw(row, parsedFilterBy) {
+ filteredRows = append(filteredRows, row)
+ keptIndices = append(keptIndices, origIdx)
+ }
+ }
+ t.rowsRawFiltered = filteredRows
+
+ // Update separators map to reflect filtered rows
+ if len(originalSeparators) > 0 {
+ newSeparators := make(map[int]bool)
+ for newIdx, origIdx := range keptIndices {
+ if originalSeparators[origIdx] {
+ newSeparators[newIdx] = true
+ }
+ }
+ t.separators = newSeparators
+ }
+}
+
func (t *Table) initForRenderRowsStringify(rows []Row, hint renderHint) []rowStr {
rowsStr := make([]rowStr, len(rows))
for idx, row := range rows {
@@ -315,36 +382,92 @@ func (t *Table) initForRenderRowPainterColors() {
return
}
- // generate the colors
- t.rowsColors = make([]text.Colors, len(t.rowsRaw))
- for idx, row := range t.rowsRaw {
- idxColors := idx
+ // generate the colors for the final rows (after filtering and sorting)
+ // rowsColors will be indexed by the final position in t.rows
+ t.rowsColors = make([]text.Colors, len(t.rows))
+
+ // For each final position, find the row index in t.rowsRawFiltered (which is already filtered)
+ for finalPos := range t.rows {
+ var rowIdx int
+
if len(t.sortedRowIndices) > 0 {
- // override with the sorted row index
- for j := 0; j < len(t.sortedRowIndices); j++ {
- if t.sortedRowIndices[j] == idx {
- idxColors = j
- break
- }
- }
+ // Rows were sorted: finalPos -> sortedRowIndices[finalPos] -> rowIdx in t.rowsRawFiltered
+ rowIdx = t.sortedRowIndices[finalPos]
+ } else {
+ // No sorting: finalPos -> rowIdx in t.rowsRawFiltered
+ rowIdx = finalPos
}
- if t.rowPainter != nil {
- t.rowsColors[idxColors] = t.rowPainter(row)
- } else if t.rowPainterWithAttributes != nil {
- t.rowsColors[idxColors] = t.rowPainterWithAttributes(row, RowAttributes{
- Number: idx + 1,
- NumberSorted: idxColors + 1,
- })
+ if rowIdx >= 0 && rowIdx < len(t.rowsRawFiltered) {
+ row := t.rowsRawFiltered[rowIdx]
+ if t.rowPainter != nil {
+ t.rowsColors[finalPos] = t.rowPainter(row)
+ } else if t.rowPainterWithAttributes != nil {
+ t.rowsColors[finalPos] = t.rowPainterWithAttributes(row, RowAttributes{
+ Number: rowIdx + 1,
+ NumberSorted: finalPos + 1,
+ })
+ }
}
}
}
func (t *Table) initForRenderRowSeparator() {
- t.rowSeparator = make(rowStr, t.numColumns)
- for colIdx, maxColumnLength := range t.maxColumnLengths {
- maxColumnLength += text.StringWidthWithoutEscSequences(t.style.Box.PaddingLeft + t.style.Box.PaddingRight)
- t.rowSeparator[colIdx] = text.RepeatAndTrim(t.style.Box.MiddleHorizontal, maxColumnLength)
+ // this is needed only for default render mode
+ if t.renderMode != renderModeDefault {
+ return
+ }
+
+ // init the separatorType -> separator-string map
+ t.initForRenderRowSeparatorStrings()
+
+ // init the separator-string -> separator-row map
+ t.rowSeparators = make(map[string]rowStr, len(t.rowSeparatorStrings))
+ paddingLength := text.StringWidthWithoutEscSequences(t.style.Box.PaddingLeft + t.style.Box.PaddingRight)
+ for _, separator := range t.rowSeparatorStrings {
+ t.rowSeparators[separator] = make(rowStr, t.numColumns)
+ for colIdx, maxColumnLength := range t.maxColumnLengths {
+ t.rowSeparators[separator][colIdx] = text.RepeatAndTrim(separator, maxColumnLength+paddingLength)
+ }
+ }
+}
+
+func (t *Table) initForRenderRowSeparatorStrings() {
+ // allocate and init only the separators that are needed
+ t.rowSeparatorStrings = make(map[separatorType]string)
+ addSeparatorType := func(st separatorType) {
+ t.rowSeparatorStrings[st] = t.style.Box.middleHorizontal(st)
+ }
+
+ // for other render modes, we need all the separators
+ if t.title != "" {
+ addSeparatorType(separatorTypeTitleTop)
+ addSeparatorType(separatorTypeTitleBottom)
+ }
+ if len(t.rowsHeader) > 0 || t.autoIndex {
+ addSeparatorType(separatorTypeHeaderTop)
+ addSeparatorType(separatorTypeHeaderBottom)
+ if len(t.rowsHeader) > 1 {
+ addSeparatorType(separatorTypeHeaderMiddle)
+ }
+ }
+ if len(t.rows) > 0 {
+ addSeparatorType(separatorTypeRowTop)
+ addSeparatorType(separatorTypeRowBottom)
+ if len(t.rows) > 1 {
+ addSeparatorType(separatorTypeRowMiddle)
+ }
+ } else if len(t.rowsHeader) > 0 || t.autoIndex {
+ // When there are headers but no data rows, we still need separatorTypeRowBottom
+ // for the bottom border.
+ addSeparatorType(separatorTypeRowBottom)
+ }
+ if len(t.rowsFooter) > 0 || t.autoIndex {
+ addSeparatorType(separatorTypeFooterTop)
+ addSeparatorType(separatorTypeFooterBottom)
+ if len(t.rowsFooter) > 1 {
+ addSeparatorType(separatorTypeFooterMiddle)
+ }
}
}
@@ -402,9 +525,10 @@ func (t *Table) reset() {
t.maxRowLength = 0
t.numColumns = 0
t.numLinesRendered = 0
- t.rowSeparator = nil
+ t.rowSeparators = nil
t.rows = nil
t.rowsColors = nil
t.rowsFooter = nil
t.rowsHeader = nil
+ t.sortedRowIndices = nil
}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/render_markdown.go b/vendor/github.com/jedib0t/go-pretty/v6/table/render_markdown.go
index adf573fc..80cdcc07 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/table/render_markdown.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/render_markdown.go
@@ -14,7 +14,7 @@ import (
// | 300 | Tyrion | Lannister | 5000 | |
// | | | Total | 10000 | |
func (t *Table) RenderMarkdown() string {
- t.initForRender()
+ t.initForRender(renderModeMarkdown)
var out strings.Builder
if t.numColumns > 0 {
@@ -47,19 +47,15 @@ func (t *Table) markdownRenderRow(out *strings.Builder, row rowStr, hint renderH
for colIdx := 0; colIdx < t.numColumns; colIdx++ {
t.markdownRenderRowAutoIndex(out, colIdx, hint)
- if hint.isSeparatorRow {
- out.WriteString(t.getAlign(colIdx, hint).MarkdownProperty())
- } else {
- var colStr string
- if colIdx < len(row) {
- colStr = row[colIdx]
- }
- out.WriteRune(' ')
- colStr = strings.ReplaceAll(colStr, "|", "\\|")
- colStr = strings.ReplaceAll(colStr, "\n", "
")
- out.WriteString(colStr)
- out.WriteRune(' ')
+ var colStr string
+ if colIdx < len(row) {
+ colStr = row[colIdx]
}
+ out.WriteRune(' ')
+ colStr = strings.ReplaceAll(colStr, "|", "\\|")
+ colStr = strings.ReplaceAll(colStr, "\n", "
")
+ out.WriteString(colStr)
+ out.WriteRune(' ')
out.WriteRune('|')
}
}
@@ -83,7 +79,7 @@ func (t *Table) markdownRenderRows(out *strings.Builder, rows []rowStr, hint ren
t.markdownRenderRow(out, row, hint)
if idx == len(rows)-1 && hint.isHeaderRow {
- t.markdownRenderRow(out, t.rowSeparator, renderHint{isSeparatorRow: true})
+ t.markdownRenderSeparator(out, renderHint{isSeparatorRow: true})
}
}
}
@@ -101,6 +97,21 @@ func (t *Table) markdownRenderRowsHeader(out *strings.Builder) {
}
}
+func (t *Table) markdownRenderSeparator(out *strings.Builder, hint renderHint) {
+ // when working on line number 2 or more, insert a newline first
+ if out.Len() > 0 {
+ out.WriteRune('\n')
+ }
+
+ out.WriteRune('|')
+ for colIdx := 0; colIdx < t.numColumns; colIdx++ {
+ t.markdownRenderRowAutoIndex(out, colIdx, hint)
+
+ out.WriteString(t.getAlign(colIdx, hint).MarkdownProperty())
+ out.WriteRune('|')
+ }
+}
+
func (t *Table) markdownRenderTitle(out *strings.Builder) {
if t.title != "" {
out.WriteString("# ")
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/render_tsv.go b/vendor/github.com/jedib0t/go-pretty/v6/table/render_tsv.go
index 67f1e7f7..312f365d 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/table/render_tsv.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/render_tsv.go
@@ -6,7 +6,7 @@ import (
)
func (t *Table) RenderTSV() string {
- t.initForRender()
+ t.initForRender(renderModeTSV)
var out strings.Builder
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/sort.go b/vendor/github.com/jedib0t/go-pretty/v6/table/sort.go
index 7a47765a..dd1d2175 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/table/sort.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/sort.go
@@ -21,6 +21,18 @@ type SortBy struct {
// IgnoreCase makes sorting case-insensitive
IgnoreCase bool
+
+ // CustomLess is a function that can be used to sort the column in a custom
+ // manner. Note that:
+ // * This overrides and ignores the Mode and IgnoreCase settings
+ // * This is called after the column contents are converted to string form
+ // * This function is expected to return:
+ // * -1 => when iStr comes before jStr
+ // * 0 => when iStr and jStr are considered equal
+ // * 1 => when iStr comes after jStr
+ //
+ // Use this when the default sorting logic is not sufficient.
+ CustomLess func(iStr string, jStr string) int
}
// SortMode defines How to sort.
@@ -49,12 +61,6 @@ const (
DscNumericAlpha
)
-type rowsSorter struct {
- rows []rowStr
- sortBy []SortBy
- sortedIndices []int
-}
-
// getSortedRowIndices sorts and returns the row indices in Sorted order as
// directed by Table.sortBy which can be set using Table.SortBy(...)
func (t *Table) getSortedRowIndices() []int {
@@ -63,11 +69,31 @@ func (t *Table) getSortedRowIndices() []int {
sortedIndices[idx] = idx
}
- if t.sortBy != nil && len(t.sortBy) > 0 {
- sort.Sort(rowsSorter{
- rows: t.rows,
- sortBy: t.parseSortBy(t.sortBy),
- sortedIndices: sortedIndices,
+ if len(t.sortBy) > 0 {
+ parsedSortBy := t.parseSortBy(t.sortBy)
+ sort.Slice(sortedIndices, func(i, j int) bool {
+ isEqual, isLess := false, false
+ realI, realJ := sortedIndices[i], sortedIndices[j]
+ for _, sortBy := range parsedSortBy {
+ // extract the values/cells from the rows for comparison
+ rowI, rowJ, colIdx := t.rows[realI], t.rows[realJ], sortBy.Number-1
+ iVal, jVal := "", ""
+ if colIdx < len(rowI) {
+ iVal = rowI[colIdx]
+ }
+ if colIdx < len(rowJ) {
+ jVal = rowJ[colIdx]
+ }
+
+ // compare and choose whether to continue
+ isEqual, isLess = less(iVal, jVal, sortBy)
+ // if the values are not equal, return the result immediately
+ if !isEqual {
+ return isLess
+ }
+ // if the values are equal, continue to the next column
+ }
+ return isLess
})
}
@@ -94,48 +120,32 @@ func (t *Table) parseSortBy(sortBy []SortBy) []SortBy {
Number: colNum,
Mode: col.Mode,
IgnoreCase: col.IgnoreCase,
+ CustomLess: col.CustomLess,
})
}
}
return resSortBy
}
-func (rs rowsSorter) Len() int {
- return len(rs.rows)
-}
-
-func (rs rowsSorter) Swap(i, j int) {
- rs.sortedIndices[i], rs.sortedIndices[j] = rs.sortedIndices[j], rs.sortedIndices[i]
-}
-
-func (rs rowsSorter) Less(i, j int) bool {
- shouldContinue, returnValue := false, false
- realI, realJ := rs.sortedIndices[i], rs.sortedIndices[j]
- for _, sortBy := range rs.sortBy {
- // extract the values/cells from the rows for comparison
- rowI, rowJ, colIdx := rs.rows[realI], rs.rows[realJ], sortBy.Number-1
- iVal, jVal := "", ""
- if colIdx < len(rowI) {
- iVal = rowI[colIdx]
- }
- if colIdx < len(rowJ) {
- jVal = rowJ[colIdx]
- }
-
- // compare and choose whether to continue
- shouldContinue, returnValue = less(iVal, jVal, sortBy)
- if !shouldContinue {
- break
+func less(iVal string, jVal string, sb SortBy) (bool, bool) {
+ if sb.CustomLess != nil {
+ // use the custom less function to compare the values
+ rc := sb.CustomLess(iVal, jVal)
+ if rc < 0 {
+ return false, true
+ } else if rc > 0 {
+ return false, false
+ } else { // rc == 0
+ return true, false
}
}
- return returnValue
-}
-func less(iVal string, jVal string, sb SortBy) (bool, bool) {
+ // if the values are equal, return fast to continue to next column
if iVal == jVal {
return true, false
}
+ // otherwise, use the default sorting logic defined by Mode and IgnoreCase
switch sb.Mode {
case Asc, Dsc:
return lessAlphabetic(iVal, jVal, sb)
@@ -168,37 +178,27 @@ func lessAlphabetic(iVal string, jVal string, sb SortBy) (bool, bool) {
}
}
-func lessAlphaNumericI(sb SortBy) (bool, bool) {
- // i == "abc"; j == 5
- switch sb.Mode {
- case AscAlphaNumeric, DscAlphaNumeric:
- return false, true
- default: // AscNumericAlpha, DscNumericAlpha
- return false, false
- }
-}
-
-func lessAlphaNumericJ(sb SortBy) (bool, bool) {
- // i == 5; j == "abc"
- switch sb.Mode {
- case AscAlphaNumeric, DscAlphaNumeric:
- return false, false
- default: // AscNumericAlpha, DscNumericAlpha:
- return false, true
- }
-}
-
func lessMixedMode(iVal string, jVal string, sb SortBy) (bool, bool) {
iNumVal, iErr := strconv.ParseFloat(iVal, 64)
jNumVal, jErr := strconv.ParseFloat(jVal, 64)
if iErr != nil && jErr != nil { // both are alphanumeric
return lessAlphabetic(iVal, jVal, sb)
}
- if iErr != nil { // iVal is alphabetic, jVal is numeric
- return lessAlphaNumericI(sb)
+ if iErr != nil { // iVal == "abc"; jVal == 5
+ switch sb.Mode {
+ case AscAlphaNumeric, DscAlphaNumeric:
+ return false, true
+ default: // AscNumericAlpha, DscNumericAlpha
+ return false, false
+ }
}
- if jErr != nil { // iVal is numeric, jVal is alphabetic
- return lessAlphaNumericJ(sb)
+ if jErr != nil { // iVal == 5; jVal == "abc"
+ switch sb.Mode {
+ case AscAlphaNumeric, DscAlphaNumeric:
+ return false, false
+ default: // AscNumericAlpha, DscNumericAlpha:
+ return false, true
+ }
}
// both values numeric
return lessNumericVal(iNumVal, jNumVal, sb)
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/style.go b/vendor/github.com/jedib0t/go-pretty/v6/table/style.go
index aaed2c27..b5f58493 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/table/style.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/style.go
@@ -1,9 +1,5 @@
package table
-import (
- "github.com/jedib0t/go-pretty/v6/text"
-)
-
// Style declares how to render the Table and provides very fine-grained control
// on how the Table gets rendered on the Console.
type Style struct {
@@ -340,591 +336,3 @@ var (
Title: TitleOptionsDefault,
}
)
-
-// BoxStyle defines the characters/strings to use to render the borders and
-// separators for the Table.
-type BoxStyle struct {
- BottomLeft string
- BottomRight string
- BottomSeparator string
- EmptySeparator string
- Left string
- LeftSeparator string
- MiddleHorizontal string
- MiddleSeparator string
- MiddleVertical string
- PaddingLeft string
- PaddingRight string
- PageSeparator string
- Right string
- RightSeparator string
- TopLeft string
- TopRight string
- TopSeparator string
- UnfinishedRow string
-}
-
-var (
- // StyleBoxDefault defines a Boxed-Table like below:
- // +-----+------------+-----------+--------+-----------------------------+
- // | # | FIRST NAME | LAST NAME | SALARY | |
- // +-----+------------+-----------+--------+-----------------------------+
- // | 1 | Arya | Stark | 3000 | |
- // | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
- // | 300 | Tyrion | Lannister | 5000 | |
- // +-----+------------+-----------+--------+-----------------------------+
- // | | | TOTAL | 10000 | |
- // +-----+------------+-----------+--------+-----------------------------+
- StyleBoxDefault = BoxStyle{
- BottomLeft: "+",
- BottomRight: "+",
- BottomSeparator: "+",
- EmptySeparator: " ",
- Left: "|",
- LeftSeparator: "+",
- MiddleHorizontal: "-",
- MiddleSeparator: "+",
- MiddleVertical: "|",
- PaddingLeft: " ",
- PaddingRight: " ",
- PageSeparator: "\n",
- Right: "|",
- RightSeparator: "+",
- TopLeft: "+",
- TopRight: "+",
- TopSeparator: "+",
- UnfinishedRow: " ~",
- }
-
- // StyleBoxBold defines a Boxed-Table like below:
- // ┏━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
- // ┃ # ┃ FIRST NAME ┃ LAST NAME ┃ SALARY ┃ ┃
- // ┣━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
- // ┃ 1 ┃ Arya ┃ Stark ┃ 3000 ┃ ┃
- // ┃ 20 ┃ Jon ┃ Snow ┃ 2000 ┃ You know nothing, Jon Snow! ┃
- // ┃ 300 ┃ Tyrion ┃ Lannister ┃ 5000 ┃ ┃
- // ┣━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
- // ┃ ┃ ┃ TOTAL ┃ 10000 ┃ ┃
- // ┗━━━━━┻━━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
- StyleBoxBold = BoxStyle{
- BottomLeft: "┗",
- BottomRight: "┛",
- BottomSeparator: "┻",
- EmptySeparator: " ",
- Left: "┃",
- LeftSeparator: "┣",
- MiddleHorizontal: "━",
- MiddleSeparator: "╋",
- MiddleVertical: "┃",
- PaddingLeft: " ",
- PaddingRight: " ",
- PageSeparator: "\n",
- Right: "┃",
- RightSeparator: "┫",
- TopLeft: "┏",
- TopRight: "┓",
- TopSeparator: "┳",
- UnfinishedRow: " ≈",
- }
-
- // StyleBoxDouble defines a Boxed-Table like below:
- // ╔═════╦════════════╦═══════════╦════════╦═════════════════════════════╗
- // ║ # ║ FIRST NAME ║ LAST NAME ║ SALARY ║ ║
- // ╠═════╬════════════╬═══════════╬════════╬═════════════════════════════╣
- // ║ 1 ║ Arya ║ Stark ║ 3000 ║ ║
- // ║ 20 ║ Jon ║ Snow ║ 2000 ║ You know nothing, Jon Snow! ║
- // ║ 300 ║ Tyrion ║ Lannister ║ 5000 ║ ║
- // ╠═════╬════════════╬═══════════╬════════╬═════════════════════════════╣
- // ║ ║ ║ TOTAL ║ 10000 ║ ║
- // ╚═════╩════════════╩═══════════╩════════╩═════════════════════════════╝
- StyleBoxDouble = BoxStyle{
- BottomLeft: "╚",
- BottomRight: "╝",
- BottomSeparator: "╩",
- EmptySeparator: " ",
- Left: "║",
- LeftSeparator: "╠",
- MiddleHorizontal: "═",
- MiddleSeparator: "╬",
- MiddleVertical: "║",
- PaddingLeft: " ",
- PaddingRight: " ",
- PageSeparator: "\n",
- Right: "║",
- RightSeparator: "╣",
- TopLeft: "╔",
- TopRight: "╗",
- TopSeparator: "╦",
- UnfinishedRow: " ≈",
- }
-
- // StyleBoxLight defines a Boxed-Table like below:
- // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
- // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
- // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
- // │ 1 │ Arya │ Stark │ 3000 │ │
- // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
- // │ 300 │ Tyrion │ Lannister │ 5000 │ │
- // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
- // │ │ │ TOTAL │ 10000 │ │
- // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
- StyleBoxLight = BoxStyle{
- BottomLeft: "└",
- BottomRight: "┘",
- BottomSeparator: "┴",
- EmptySeparator: " ",
- Left: "│",
- LeftSeparator: "├",
- MiddleHorizontal: "─",
- MiddleSeparator: "┼",
- MiddleVertical: "│",
- PaddingLeft: " ",
- PaddingRight: " ",
- PageSeparator: "\n",
- Right: "│",
- RightSeparator: "┤",
- TopLeft: "┌",
- TopRight: "┐",
- TopSeparator: "┬",
- UnfinishedRow: " ≈",
- }
-
- // StyleBoxRounded defines a Boxed-Table like below:
- // ╭─────┬────────────┬───────────┬────────┬─────────────────────────────╮
- // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
- // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
- // │ 1 │ Arya │ Stark │ 3000 │ │
- // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
- // │ 300 │ Tyrion │ Lannister │ 5000 │ │
- // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
- // │ │ │ TOTAL │ 10000 │ │
- // ╰─────┴────────────┴───────────┴────────┴─────────────────────────────╯
- StyleBoxRounded = BoxStyle{
- BottomLeft: "╰",
- BottomRight: "╯",
- BottomSeparator: "┴",
- EmptySeparator: " ",
- Left: "│",
- LeftSeparator: "├",
- MiddleHorizontal: "─",
- MiddleSeparator: "┼",
- MiddleVertical: "│",
- PaddingLeft: " ",
- PaddingRight: " ",
- PageSeparator: "\n",
- Right: "│",
- RightSeparator: "┤",
- TopLeft: "╭",
- TopRight: "╮",
- TopSeparator: "┬",
- UnfinishedRow: " ≈",
- }
-
- // styleBoxTest defines a Boxed-Table like below:
- // (-----^------------^-----------^--------^-----------------------------)
- // [< #>||||< >]
- // {-----+------------+-----------+--------+-----------------------------}
- // [< 1>|||< 3000>|< >]
- // [< 20>|||< 2000>|]
- // [<300>|||< 5000>|< >]
- // {-----+------------+-----------+--------+-----------------------------}
- // [< >|< >||< 10000>|< >]
- // \-----v------------v-----------v--------v-----------------------------/
- styleBoxTest = BoxStyle{
- BottomLeft: "\\",
- BottomRight: "/",
- BottomSeparator: "v",
- EmptySeparator: " ",
- Left: "[",
- LeftSeparator: "{",
- MiddleHorizontal: "--",
- MiddleSeparator: "+",
- MiddleVertical: "|",
- PaddingLeft: "<",
- PaddingRight: ">",
- PageSeparator: "\n",
- Right: "]",
- RightSeparator: "}",
- TopLeft: "(",
- TopRight: ")",
- TopSeparator: "^",
- UnfinishedRow: " ~~~",
- }
-)
-
-// ColorOptions defines the ANSI colors to use for parts of the Table.
-type ColorOptions struct {
- Border text.Colors // borders (if nil, uses one of the below)
- Footer text.Colors // footer row(s) colors
- Header text.Colors // header row(s) colors
- IndexColumn text.Colors // index-column colors (row #, etc.)
- Row text.Colors // regular row(s) colors
- RowAlternate text.Colors // regular row(s) colors for the even-numbered rows
- Separator text.Colors // separators (if nil, uses one of the above)
-}
-
-var (
- // ColorOptionsDefault defines sensible ANSI color options - basically NONE.
- ColorOptionsDefault = ColorOptions{}
-
- // ColorOptionsBright renders dark text on bright background.
- ColorOptionsBright = ColorOptionsBlackOnCyanWhite
-
- // ColorOptionsDark renders bright text on dark background.
- ColorOptionsDark = ColorOptionsCyanWhiteOnBlack
-
- // ColorOptionsBlackOnBlueWhite renders Black text on Blue/White background.
- ColorOptionsBlackOnBlueWhite = ColorOptions{
- Footer: text.Colors{text.BgBlue, text.FgBlack},
- Header: text.Colors{text.BgHiBlue, text.FgBlack},
- IndexColumn: text.Colors{text.BgHiBlue, text.FgBlack},
- Row: text.Colors{text.BgHiWhite, text.FgBlack},
- RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
- }
-
- // ColorOptionsBlackOnCyanWhite renders Black text on Cyan/White background.
- ColorOptionsBlackOnCyanWhite = ColorOptions{
- Footer: text.Colors{text.BgCyan, text.FgBlack},
- Header: text.Colors{text.BgHiCyan, text.FgBlack},
- IndexColumn: text.Colors{text.BgHiCyan, text.FgBlack},
- Row: text.Colors{text.BgHiWhite, text.FgBlack},
- RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
- }
-
- // ColorOptionsBlackOnGreenWhite renders Black text on Green/White
- // background.
- ColorOptionsBlackOnGreenWhite = ColorOptions{
- Footer: text.Colors{text.BgGreen, text.FgBlack},
- Header: text.Colors{text.BgHiGreen, text.FgBlack},
- IndexColumn: text.Colors{text.BgHiGreen, text.FgBlack},
- Row: text.Colors{text.BgHiWhite, text.FgBlack},
- RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
- }
-
- // ColorOptionsBlackOnMagentaWhite renders Black text on Magenta/White
- // background.
- ColorOptionsBlackOnMagentaWhite = ColorOptions{
- Footer: text.Colors{text.BgMagenta, text.FgBlack},
- Header: text.Colors{text.BgHiMagenta, text.FgBlack},
- IndexColumn: text.Colors{text.BgHiMagenta, text.FgBlack},
- Row: text.Colors{text.BgHiWhite, text.FgBlack},
- RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
- }
-
- // ColorOptionsBlackOnRedWhite renders Black text on Red/White background.
- ColorOptionsBlackOnRedWhite = ColorOptions{
- Footer: text.Colors{text.BgRed, text.FgBlack},
- Header: text.Colors{text.BgHiRed, text.FgBlack},
- IndexColumn: text.Colors{text.BgHiRed, text.FgBlack},
- Row: text.Colors{text.BgHiWhite, text.FgBlack},
- RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
- }
-
- // ColorOptionsBlackOnYellowWhite renders Black text on Yellow/White
- // background.
- ColorOptionsBlackOnYellowWhite = ColorOptions{
- Footer: text.Colors{text.BgYellow, text.FgBlack},
- Header: text.Colors{text.BgHiYellow, text.FgBlack},
- IndexColumn: text.Colors{text.BgHiYellow, text.FgBlack},
- Row: text.Colors{text.BgHiWhite, text.FgBlack},
- RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
- }
-
- // ColorOptionsBlueWhiteOnBlack renders Blue/White text on Black background.
- ColorOptionsBlueWhiteOnBlack = ColorOptions{
- Footer: text.Colors{text.FgBlue, text.BgHiBlack},
- Header: text.Colors{text.FgHiBlue, text.BgHiBlack},
- IndexColumn: text.Colors{text.FgHiBlue, text.BgHiBlack},
- Row: text.Colors{text.FgHiWhite, text.BgBlack},
- RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
- }
-
- // ColorOptionsCyanWhiteOnBlack renders Cyan/White text on Black background.
- ColorOptionsCyanWhiteOnBlack = ColorOptions{
- Footer: text.Colors{text.FgCyan, text.BgHiBlack},
- Header: text.Colors{text.FgHiCyan, text.BgHiBlack},
- IndexColumn: text.Colors{text.FgHiCyan, text.BgHiBlack},
- Row: text.Colors{text.FgHiWhite, text.BgBlack},
- RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
- }
-
- // ColorOptionsGreenWhiteOnBlack renders Green/White text on Black
- // background.
- ColorOptionsGreenWhiteOnBlack = ColorOptions{
- Footer: text.Colors{text.FgGreen, text.BgHiBlack},
- Header: text.Colors{text.FgHiGreen, text.BgHiBlack},
- IndexColumn: text.Colors{text.FgHiGreen, text.BgHiBlack},
- Row: text.Colors{text.FgHiWhite, text.BgBlack},
- RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
- }
-
- // ColorOptionsMagentaWhiteOnBlack renders Magenta/White text on Black
- // background.
- ColorOptionsMagentaWhiteOnBlack = ColorOptions{
- Footer: text.Colors{text.FgMagenta, text.BgHiBlack},
- Header: text.Colors{text.FgHiMagenta, text.BgHiBlack},
- IndexColumn: text.Colors{text.FgHiMagenta, text.BgHiBlack},
- Row: text.Colors{text.FgHiWhite, text.BgBlack},
- RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
- }
-
- // ColorOptionsRedWhiteOnBlack renders Red/White text on Black background.
- ColorOptionsRedWhiteOnBlack = ColorOptions{
- Footer: text.Colors{text.FgRed, text.BgHiBlack},
- Header: text.Colors{text.FgHiRed, text.BgHiBlack},
- IndexColumn: text.Colors{text.FgHiRed, text.BgHiBlack},
- Row: text.Colors{text.FgHiWhite, text.BgBlack},
- RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
- }
-
- // ColorOptionsYellowWhiteOnBlack renders Yellow/White text on Black
- // background.
- ColorOptionsYellowWhiteOnBlack = ColorOptions{
- Footer: text.Colors{text.FgYellow, text.BgHiBlack},
- Header: text.Colors{text.FgHiYellow, text.BgHiBlack},
- IndexColumn: text.Colors{text.FgHiYellow, text.BgHiBlack},
- Row: text.Colors{text.FgHiWhite, text.BgBlack},
- RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
- }
-)
-
-// FormatOptions defines the text-formatting to perform on parts of the Table.
-type FormatOptions struct {
- Direction text.Direction // (forced) BiDi direction for each Column
- Footer text.Format // default text format
- FooterAlign text.Align // default horizontal align
- FooterVAlign text.VAlign // default vertical align
- Header text.Format // default text format
- HeaderAlign text.Align // default horizontal align
- HeaderVAlign text.VAlign // default vertical align
- Row text.Format // default text format
- RowAlign text.Align // default horizontal align
- RowVAlign text.VAlign // default vertical align
-}
-
-// FormatOptionsDefault defines sensible formatting options.
-var FormatOptionsDefault = FormatOptions{
- Footer: text.FormatUpper,
- FooterAlign: text.AlignDefault,
- FooterVAlign: text.VAlignDefault,
- Header: text.FormatUpper,
- HeaderAlign: text.AlignDefault,
- HeaderVAlign: text.VAlignDefault,
- Row: text.FormatDefault,
- RowAlign: text.AlignDefault,
- RowVAlign: text.VAlignDefault,
-}
-
-// HTMLOptions defines the global options to control HTML rendering.
-type HTMLOptions struct {
- CSSClass string // CSS class to set on the overall tag
- EmptyColumn string // string to replace "" columns with (entire content being "")
- EscapeText bool // escape text into HTML-safe content?
- Newline string // string to replace "\n" characters with
-}
-
-// DefaultHTMLOptions defines sensible HTML rendering defaults.
-var DefaultHTMLOptions = HTMLOptions{
- CSSClass: DefaultHTMLCSSClass,
- EmptyColumn: " ",
- EscapeText: true,
- Newline: "
",
-}
-
-// Options defines the global options that determine how the Table is
-// rendered.
-type Options struct {
- // DoNotColorBordersAndSeparators disables coloring all the borders and row
- // or column separators.
- DoNotColorBordersAndSeparators bool
-
- // DrawBorder enables or disables drawing the border around the Table.
- // Example of a table where it is disabled:
- // # │ FIRST NAME │ LAST NAME │ SALARY │
- // ─────┼────────────┼───────────┼────────┼─────────────────────────────
- // 1 │ Arya │ Stark │ 3000 │
- // 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow!
- // 300 │ Tyrion │ Lannister │ 5000 │
- // ─────┼────────────┼───────────┼────────┼─────────────────────────────
- // │ │ TOTAL │ 10000 │
- DrawBorder bool
-
- // SeparateColumns enables or disable drawing border between columns.
- // Example of a table where it is disabled:
- // ┌─────────────────────────────────────────────────────────────────┐
- // │ # FIRST NAME LAST NAME SALARY │
- // ├─────────────────────────────────────────────────────────────────┤
- // │ 1 Arya Stark 3000 │
- // │ 20 Jon Snow 2000 You know nothing, Jon Snow! │
- // │ 300 Tyrion Lannister 5000 │
- // │ TOTAL 10000 │
- // └─────────────────────────────────────────────────────────────────┘
- SeparateColumns bool
-
- // SeparateFooter enables or disable drawing border between the footer and
- // the rows. Example of a table where it is disabled:
- // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
- // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
- // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
- // │ 1 │ Arya │ Stark │ 3000 │ │
- // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
- // │ 300 │ Tyrion │ Lannister │ 5000 │ │
- // │ │ │ TOTAL │ 10000 │ │
- // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
- SeparateFooter bool
-
- // SeparateHeader enables or disable drawing border between the header and
- // the rows. Example of a table where it is disabled:
- // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
- // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
- // │ 1 │ Arya │ Stark │ 3000 │ │
- // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
- // │ 300 │ Tyrion │ Lannister │ 5000 │ │
- // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
- // │ │ │ TOTAL │ 10000 │ │
- // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
- SeparateHeader bool
-
- // SeparateRows enables or disables drawing separators between each row.
- // Example of a table where it is enabled:
- // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
- // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
- // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
- // │ 1 │ Arya │ Stark │ 3000 │ │
- // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
- // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
- // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
- // │ 300 │ Tyrion │ Lannister │ 5000 │ │
- // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
- // │ │ │ TOTAL │ 10000 │ │
- // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
- SeparateRows bool
-}
-
-var (
- // OptionsDefault defines sensible global options.
- OptionsDefault = Options{
- DrawBorder: true,
- SeparateColumns: true,
- SeparateFooter: true,
- SeparateHeader: true,
- SeparateRows: false,
- }
-
- // OptionsNoBorders sets up a table without any borders.
- OptionsNoBorders = Options{
- DrawBorder: false,
- SeparateColumns: true,
- SeparateFooter: true,
- SeparateHeader: true,
- SeparateRows: false,
- }
-
- // OptionsNoBordersAndSeparators sets up a table without any borders or
- // separators.
- OptionsNoBordersAndSeparators = Options{
- DrawBorder: false,
- SeparateColumns: false,
- SeparateFooter: false,
- SeparateHeader: false,
- SeparateRows: false,
- }
-)
-
-// SizeOptions defines the way to control the width of the table output.
-type SizeOptions struct {
- // WidthMax is the maximum allotted width for the full row;
- // any content beyond this will be truncated using the text
- // in Style.Box.UnfinishedRow
- WidthMax int
- // WidthMin is the minimum allotted width for the full row;
- // columns will be auto-expanded until the overall width
- // is met
- WidthMin int
-}
-
-var (
- // SizeOptionsDefault defines sensible size options - basically NONE.
- SizeOptionsDefault = SizeOptions{
- WidthMax: 0,
- WidthMin: 0,
- }
-)
-
-// TitleOptions defines the way the title text is to be rendered.
-type TitleOptions struct {
- Align text.Align
- Colors text.Colors
- Format text.Format
-}
-
-var (
- // TitleOptionsDefault defines sensible title options - basically NONE.
- TitleOptionsDefault = TitleOptions{}
-
- // TitleOptionsBright renders Bright Bold text on Dark background.
- TitleOptionsBright = TitleOptionsBlackOnCyan
-
- // TitleOptionsDark renders Dark Bold text on Bright background.
- TitleOptionsDark = TitleOptionsCyanOnBlack
-
- // TitleOptionsBlackOnBlue renders Black text on Blue background.
- TitleOptionsBlackOnBlue = TitleOptions{
- Colors: append(ColorOptionsBlackOnBlueWhite.Header, text.Bold),
- }
-
- // TitleOptionsBlackOnCyan renders Black Bold text on Cyan background.
- TitleOptionsBlackOnCyan = TitleOptions{
- Colors: append(ColorOptionsBlackOnCyanWhite.Header, text.Bold),
- }
-
- // TitleOptionsBlackOnGreen renders Black Bold text onGreen background.
- TitleOptionsBlackOnGreen = TitleOptions{
- Colors: append(ColorOptionsBlackOnGreenWhite.Header, text.Bold),
- }
-
- // TitleOptionsBlackOnMagenta renders Black Bold text on Magenta background.
- TitleOptionsBlackOnMagenta = TitleOptions{
- Colors: append(ColorOptionsBlackOnMagentaWhite.Header, text.Bold),
- }
-
- // TitleOptionsBlackOnRed renders Black Bold text on Red background.
- TitleOptionsBlackOnRed = TitleOptions{
- Colors: append(ColorOptionsBlackOnRedWhite.Header, text.Bold),
- }
-
- // TitleOptionsBlackOnYellow renders Black Bold text on Yellow background.
- TitleOptionsBlackOnYellow = TitleOptions{
- Colors: append(ColorOptionsBlackOnYellowWhite.Header, text.Bold),
- }
-
- // TitleOptionsBlueOnBlack renders Blue Bold text on Black background.
- TitleOptionsBlueOnBlack = TitleOptions{
- Colors: append(ColorOptionsBlueWhiteOnBlack.Header, text.Bold),
- }
-
- // TitleOptionsCyanOnBlack renders Cyan Bold text on Black background.
- TitleOptionsCyanOnBlack = TitleOptions{
- Colors: append(ColorOptionsCyanWhiteOnBlack.Header, text.Bold),
- }
-
- // TitleOptionsGreenOnBlack renders Green Bold text on Black background.
- TitleOptionsGreenOnBlack = TitleOptions{
- Colors: append(ColorOptionsGreenWhiteOnBlack.Header, text.Bold),
- }
-
- // TitleOptionsMagentaOnBlack renders Magenta Bold text on Black background.
- TitleOptionsMagentaOnBlack = TitleOptions{
- Colors: append(ColorOptionsMagentaWhiteOnBlack.Header, text.Bold),
- }
-
- // TitleOptionsRedOnBlack renders Red Bold text on Black background.
- TitleOptionsRedOnBlack = TitleOptions{
- Colors: append(ColorOptionsRedWhiteOnBlack.Header, text.Bold),
- }
-
- // TitleOptionsYellowOnBlack renders Yellow Bold text on Black background.
- TitleOptionsYellowOnBlack = TitleOptions{
- Colors: append(ColorOptionsYellowWhiteOnBlack.Header, text.Bold),
- }
-)
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/style_box.go b/vendor/github.com/jedib0t/go-pretty/v6/table/style_box.go
new file mode 100644
index 00000000..e54f3c62
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/style_box.go
@@ -0,0 +1,303 @@
+package table
+
+// BoxStyle defines the characters/strings to use to render the borders and
+// separators for the Table.
+type BoxStyle struct {
+ BottomLeft string
+ BottomRight string
+ BottomSeparator string
+ EmptySeparator string
+ Left string
+ LeftSeparator string
+ MiddleHorizontal string
+ MiddleSeparator string
+ MiddleVertical string
+ PaddingLeft string
+ PaddingRight string
+ PageSeparator string
+ Right string
+ RightSeparator string
+ TopLeft string
+ TopRight string
+ TopSeparator string
+ UnfinishedRow string
+
+ // Horizontal lets you customize the horizontal lines for the Table
+ // in a more granular way than the MiddleHorizontal string. Setting
+ // this to a non-nil value will override MiddleHorizontal.
+ Horizontal *BoxStyleHorizontal
+}
+
+// BoxStyleHorizontal defines the characters/strings to use to render the
+// horizontal lines for the Table.
+type BoxStyleHorizontal struct {
+ TitleTop string
+ TitleBottom string // overrides HeaderTop/RowTop
+ HeaderTop string
+ HeaderMiddle string
+ HeaderBottom string // overrides RowTop
+ RowTop string
+ RowMiddle string
+ RowBottom string
+ FooterTop string // overrides RowBottom
+ FooterMiddle string
+ FooterBottom string
+}
+
+// NewBoxStyleHorizontal creates a new BoxStyleHorizontal with the given
+// horizontal string.
+func NewBoxStyleHorizontal(horizontal string) *BoxStyleHorizontal {
+ return &BoxStyleHorizontal{
+ TitleTop: horizontal,
+ TitleBottom: horizontal,
+ HeaderTop: horizontal,
+ HeaderMiddle: horizontal,
+ HeaderBottom: horizontal,
+ RowTop: horizontal,
+ RowMiddle: horizontal,
+ RowBottom: horizontal,
+ FooterTop: horizontal,
+ FooterMiddle: horizontal,
+ FooterBottom: horizontal,
+ }
+}
+
+var (
+ // StyleBoxDefault defines a Boxed-Table like below:
+ // +-----+------------+-----------+--------+-----------------------------+
+ // | # | FIRST NAME | LAST NAME | SALARY | |
+ // +-----+------------+-----------+--------+-----------------------------+
+ // | 1 | Arya | Stark | 3000 | |
+ // | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
+ // | 300 | Tyrion | Lannister | 5000 | |
+ // +-----+------------+-----------+--------+-----------------------------+
+ // | | | TOTAL | 10000 | |
+ // +-----+------------+-----------+--------+-----------------------------+
+ StyleBoxDefault = BoxStyle{
+ BottomLeft: "+",
+ BottomRight: "+",
+ BottomSeparator: "+",
+ EmptySeparator: " ",
+ Left: "|",
+ LeftSeparator: "+",
+ MiddleHorizontal: "-",
+ MiddleSeparator: "+",
+ MiddleVertical: "|",
+ PaddingLeft: " ",
+ PaddingRight: " ",
+ PageSeparator: "\n",
+ Right: "|",
+ RightSeparator: "+",
+ TopLeft: "+",
+ TopRight: "+",
+ TopSeparator: "+",
+ UnfinishedRow: " ~",
+ }
+
+ // StyleBoxBold defines a Boxed-Table like below:
+ // ┏━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ // ┃ # ┃ FIRST NAME ┃ LAST NAME ┃ SALARY ┃ ┃
+ // ┣━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
+ // ┃ 1 ┃ Arya ┃ Stark ┃ 3000 ┃ ┃
+ // ┃ 20 ┃ Jon ┃ Snow ┃ 2000 ┃ You know nothing, Jon Snow! ┃
+ // ┃ 300 ┃ Tyrion ┃ Lannister ┃ 5000 ┃ ┃
+ // ┣━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
+ // ┃ ┃ ┃ TOTAL ┃ 10000 ┃ ┃
+ // ┗━━━━━┻━━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ StyleBoxBold = BoxStyle{
+ BottomLeft: "┗",
+ BottomRight: "┛",
+ BottomSeparator: "┻",
+ EmptySeparator: " ",
+ Left: "┃",
+ LeftSeparator: "┣",
+ MiddleHorizontal: "━",
+ MiddleSeparator: "╋",
+ MiddleVertical: "┃",
+ PaddingLeft: " ",
+ PaddingRight: " ",
+ PageSeparator: "\n",
+ Right: "┃",
+ RightSeparator: "┫",
+ TopLeft: "┏",
+ TopRight: "┓",
+ TopSeparator: "┳",
+ UnfinishedRow: " ≈",
+ }
+
+ // StyleBoxDouble defines a Boxed-Table like below:
+ // ╔═════╦════════════╦═══════════╦════════╦═════════════════════════════╗
+ // ║ # ║ FIRST NAME ║ LAST NAME ║ SALARY ║ ║
+ // ╠═════╬════════════╬═══════════╬════════╬═════════════════════════════╣
+ // ║ 1 ║ Arya ║ Stark ║ 3000 ║ ║
+ // ║ 20 ║ Jon ║ Snow ║ 2000 ║ You know nothing, Jon Snow! ║
+ // ║ 300 ║ Tyrion ║ Lannister ║ 5000 ║ ║
+ // ╠═════╬════════════╬═══════════╬════════╬═════════════════════════════╣
+ // ║ ║ ║ TOTAL ║ 10000 ║ ║
+ // ╚═════╩════════════╩═══════════╩════════╩═════════════════════════════╝
+ StyleBoxDouble = BoxStyle{
+ BottomLeft: "╚",
+ BottomRight: "╝",
+ BottomSeparator: "╩",
+ EmptySeparator: " ",
+ Left: "║",
+ LeftSeparator: "╠",
+ MiddleHorizontal: "═",
+ MiddleSeparator: "╬",
+ MiddleVertical: "║",
+ PaddingLeft: " ",
+ PaddingRight: " ",
+ PageSeparator: "\n",
+ Right: "║",
+ RightSeparator: "╣",
+ TopLeft: "╔",
+ TopRight: "╗",
+ TopSeparator: "╦",
+ UnfinishedRow: " ≈",
+ }
+
+ // StyleBoxLight defines a Boxed-Table like below:
+ // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
+ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ 1 │ Arya │ Stark │ 3000 │ │
+ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+ // │ 300 │ Tyrion │ Lannister │ 5000 │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ │ │ TOTAL │ 10000 │ │
+ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
+ StyleBoxLight = BoxStyle{
+ BottomLeft: "└",
+ BottomRight: "┘",
+ BottomSeparator: "┴",
+ EmptySeparator: " ",
+ Left: "│",
+ LeftSeparator: "├",
+ MiddleHorizontal: "─",
+ MiddleSeparator: "┼",
+ MiddleVertical: "│",
+ PaddingLeft: " ",
+ PaddingRight: " ",
+ PageSeparator: "\n",
+ Right: "│",
+ RightSeparator: "┤",
+ TopLeft: "┌",
+ TopRight: "┐",
+ TopSeparator: "┬",
+ UnfinishedRow: " ≈",
+ }
+
+ // StyleBoxRounded defines a Boxed-Table like below:
+ // ╭─────┬────────────┬───────────┬────────┬─────────────────────────────╮
+ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ 1 │ Arya │ Stark │ 3000 │ │
+ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+ // │ 300 │ Tyrion │ Lannister │ 5000 │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ │ │ TOTAL │ 10000 │ │
+ // ╰─────┴────────────┴───────────┴────────┴─────────────────────────────╯
+ StyleBoxRounded = BoxStyle{
+ BottomLeft: "╰",
+ BottomRight: "╯",
+ BottomSeparator: "┴",
+ EmptySeparator: " ",
+ Left: "│",
+ LeftSeparator: "├",
+ MiddleHorizontal: "─",
+ MiddleSeparator: "┼",
+ MiddleVertical: "│",
+ PaddingLeft: " ",
+ PaddingRight: " ",
+ PageSeparator: "\n",
+ Right: "│",
+ RightSeparator: "┤",
+ TopLeft: "╭",
+ TopRight: "╮",
+ TopSeparator: "┬",
+ UnfinishedRow: " ≈",
+ }
+
+ // styleBoxTest defines a Boxed-Table like below:
+ // (-----^------------^-----------^--------^-----------------------------)
+ // [< #>||||< >]
+ // {-----+------------+-----------+--------+-----------------------------}
+ // [< 1>|||< 3000>|< >]
+ // [< 20>|||< 2000>|]
+ // [<300>|||< 5000>|< >]
+ // {-----+------------+-----------+--------+-----------------------------}
+ // [< >|< >||< 10000>|< >]
+ // \-----v------------v-----------v--------v-----------------------------/
+ styleBoxTest = BoxStyle{
+ BottomLeft: "\\",
+ BottomRight: "/",
+ BottomSeparator: "v",
+ EmptySeparator: " ",
+ Left: "[",
+ LeftSeparator: "{",
+ MiddleHorizontal: "--",
+ MiddleSeparator: "+",
+ MiddleVertical: "|",
+ PaddingLeft: "<",
+ PaddingRight: ">",
+ PageSeparator: "\n",
+ Right: "]",
+ RightSeparator: "}",
+ TopLeft: "(",
+ TopRight: ")",
+ TopSeparator: "^",
+ UnfinishedRow: " ~~~",
+ }
+)
+
+type separatorType int
+
+const (
+ separatorTypeTitleTop separatorType = iota
+ separatorTypeTitleBottom
+ separatorTypeHeaderTop
+ separatorTypeHeaderMiddle
+ separatorTypeHeaderBottom
+ separatorTypeRowTop
+ separatorTypeRowMiddle
+ separatorTypeRowBottom
+ separatorTypeFooterTop
+ separatorTypeFooterMiddle
+ separatorTypeFooterBottom
+ separatorTypeCount // this should be the last value
+)
+
+func (bs *BoxStyle) ensureHorizontalInitialized() {
+ if bs.Horizontal == nil {
+ bs.Horizontal = NewBoxStyleHorizontal(bs.MiddleHorizontal)
+ }
+}
+
+func (bs *BoxStyle) middleHorizontal(st separatorType) string {
+ bs.ensureHorizontalInitialized()
+
+ switch st {
+ case separatorTypeTitleTop:
+ return bs.Horizontal.TitleTop
+ case separatorTypeTitleBottom:
+ return bs.Horizontal.TitleBottom
+ case separatorTypeHeaderTop:
+ return bs.Horizontal.HeaderTop
+ case separatorTypeHeaderMiddle:
+ return bs.Horizontal.HeaderMiddle
+ case separatorTypeHeaderBottom:
+ return bs.Horizontal.HeaderBottom
+ case separatorTypeRowTop:
+ return bs.Horizontal.RowTop
+ case separatorTypeRowBottom:
+ return bs.Horizontal.RowBottom
+ case separatorTypeFooterTop:
+ return bs.Horizontal.FooterTop
+ case separatorTypeFooterMiddle:
+ return bs.Horizontal.FooterMiddle
+ case separatorTypeFooterBottom:
+ return bs.Horizontal.FooterBottom
+ default:
+ return bs.Horizontal.RowMiddle
+ }
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/style_color.go b/vendor/github.com/jedib0t/go-pretty/v6/table/style_color.go
new file mode 100644
index 00000000..9f3ed2f2
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/style_color.go
@@ -0,0 +1,139 @@
+package table
+
+import "github.com/jedib0t/go-pretty/v6/text"
+
+// ColorOptions defines the ANSI colors to use for parts of the Table.
+type ColorOptions struct {
+ Border text.Colors // borders (if nil, uses one of the below)
+ Footer text.Colors // footer row(s) colors
+ Header text.Colors // header row(s) colors
+ IndexColumn text.Colors // index-column colors (row #, etc.)
+ Row text.Colors // regular row(s) colors
+ RowAlternate text.Colors // regular row(s) colors for the even-numbered rows
+ Separator text.Colors // separators (if nil, uses one of the above)
+}
+
+var (
+ // ColorOptionsDefault defines sensible ANSI color options - basically NONE.
+ ColorOptionsDefault = ColorOptions{}
+
+ // ColorOptionsBright renders dark text on bright background.
+ ColorOptionsBright = ColorOptionsBlackOnCyanWhite
+
+ // ColorOptionsDark renders bright text on dark background.
+ ColorOptionsDark = ColorOptionsCyanWhiteOnBlack
+
+ // ColorOptionsBlackOnBlueWhite renders Black text on Blue/White background.
+ ColorOptionsBlackOnBlueWhite = ColorOptions{
+ Footer: text.Colors{text.BgBlue, text.FgBlack},
+ Header: text.Colors{text.BgHiBlue, text.FgBlack},
+ IndexColumn: text.Colors{text.BgHiBlue, text.FgBlack},
+ Row: text.Colors{text.BgHiWhite, text.FgBlack},
+ RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
+ }
+
+ // ColorOptionsBlackOnCyanWhite renders Black text on Cyan/White background.
+ ColorOptionsBlackOnCyanWhite = ColorOptions{
+ Footer: text.Colors{text.BgCyan, text.FgBlack},
+ Header: text.Colors{text.BgHiCyan, text.FgBlack},
+ IndexColumn: text.Colors{text.BgHiCyan, text.FgBlack},
+ Row: text.Colors{text.BgHiWhite, text.FgBlack},
+ RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
+ }
+
+ // ColorOptionsBlackOnGreenWhite renders Black text on Green/White
+ // background.
+ ColorOptionsBlackOnGreenWhite = ColorOptions{
+ Footer: text.Colors{text.BgGreen, text.FgBlack},
+ Header: text.Colors{text.BgHiGreen, text.FgBlack},
+ IndexColumn: text.Colors{text.BgHiGreen, text.FgBlack},
+ Row: text.Colors{text.BgHiWhite, text.FgBlack},
+ RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
+ }
+
+ // ColorOptionsBlackOnMagentaWhite renders Black text on Magenta/White
+ // background.
+ ColorOptionsBlackOnMagentaWhite = ColorOptions{
+ Footer: text.Colors{text.BgMagenta, text.FgBlack},
+ Header: text.Colors{text.BgHiMagenta, text.FgBlack},
+ IndexColumn: text.Colors{text.BgHiMagenta, text.FgBlack},
+ Row: text.Colors{text.BgHiWhite, text.FgBlack},
+ RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
+ }
+
+ // ColorOptionsBlackOnRedWhite renders Black text on Red/White background.
+ ColorOptionsBlackOnRedWhite = ColorOptions{
+ Footer: text.Colors{text.BgRed, text.FgBlack},
+ Header: text.Colors{text.BgHiRed, text.FgBlack},
+ IndexColumn: text.Colors{text.BgHiRed, text.FgBlack},
+ Row: text.Colors{text.BgHiWhite, text.FgBlack},
+ RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
+ }
+
+ // ColorOptionsBlackOnYellowWhite renders Black text on Yellow/White
+ // background.
+ ColorOptionsBlackOnYellowWhite = ColorOptions{
+ Footer: text.Colors{text.BgYellow, text.FgBlack},
+ Header: text.Colors{text.BgHiYellow, text.FgBlack},
+ IndexColumn: text.Colors{text.BgHiYellow, text.FgBlack},
+ Row: text.Colors{text.BgHiWhite, text.FgBlack},
+ RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
+ }
+
+ // ColorOptionsBlueWhiteOnBlack renders Blue/White text on Black background.
+ ColorOptionsBlueWhiteOnBlack = ColorOptions{
+ Footer: text.Colors{text.FgBlue, text.BgHiBlack},
+ Header: text.Colors{text.FgHiBlue, text.BgHiBlack},
+ IndexColumn: text.Colors{text.FgHiBlue, text.BgHiBlack},
+ Row: text.Colors{text.FgHiWhite, text.BgBlack},
+ RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
+ }
+
+ // ColorOptionsCyanWhiteOnBlack renders Cyan/White text on Black background.
+ ColorOptionsCyanWhiteOnBlack = ColorOptions{
+ Footer: text.Colors{text.FgCyan, text.BgHiBlack},
+ Header: text.Colors{text.FgHiCyan, text.BgHiBlack},
+ IndexColumn: text.Colors{text.FgHiCyan, text.BgHiBlack},
+ Row: text.Colors{text.FgHiWhite, text.BgBlack},
+ RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
+ }
+
+ // ColorOptionsGreenWhiteOnBlack renders Green/White text on Black
+ // background.
+ ColorOptionsGreenWhiteOnBlack = ColorOptions{
+ Footer: text.Colors{text.FgGreen, text.BgHiBlack},
+ Header: text.Colors{text.FgHiGreen, text.BgHiBlack},
+ IndexColumn: text.Colors{text.FgHiGreen, text.BgHiBlack},
+ Row: text.Colors{text.FgHiWhite, text.BgBlack},
+ RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
+ }
+
+ // ColorOptionsMagentaWhiteOnBlack renders Magenta/White text on Black
+ // background.
+ ColorOptionsMagentaWhiteOnBlack = ColorOptions{
+ Footer: text.Colors{text.FgMagenta, text.BgHiBlack},
+ Header: text.Colors{text.FgHiMagenta, text.BgHiBlack},
+ IndexColumn: text.Colors{text.FgHiMagenta, text.BgHiBlack},
+ Row: text.Colors{text.FgHiWhite, text.BgBlack},
+ RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
+ }
+
+ // ColorOptionsRedWhiteOnBlack renders Red/White text on Black background.
+ ColorOptionsRedWhiteOnBlack = ColorOptions{
+ Footer: text.Colors{text.FgRed, text.BgHiBlack},
+ Header: text.Colors{text.FgHiRed, text.BgHiBlack},
+ IndexColumn: text.Colors{text.FgHiRed, text.BgHiBlack},
+ Row: text.Colors{text.FgHiWhite, text.BgBlack},
+ RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
+ }
+
+ // ColorOptionsYellowWhiteOnBlack renders Yellow/White text on Black
+ // background.
+ ColorOptionsYellowWhiteOnBlack = ColorOptions{
+ Footer: text.Colors{text.FgYellow, text.BgHiBlack},
+ Header: text.Colors{text.FgHiYellow, text.BgHiBlack},
+ IndexColumn: text.Colors{text.FgHiYellow, text.BgHiBlack},
+ Row: text.Colors{text.FgHiWhite, text.BgBlack},
+ RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
+ }
+)
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/style_format.go b/vendor/github.com/jedib0t/go-pretty/v6/table/style_format.go
new file mode 100644
index 00000000..aa3ba08a
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/style_format.go
@@ -0,0 +1,32 @@
+package table
+
+import "github.com/jedib0t/go-pretty/v6/text"
+
+// FormatOptions defines the text-formatting to perform on parts of the Table.
+type FormatOptions struct {
+ Direction text.Direction // (forced) BiDi direction for each Column
+ Footer text.Format // default text format
+ FooterAlign text.Align // default horizontal align
+ FooterVAlign text.VAlign // default vertical align
+ Header text.Format // default text format
+ HeaderAlign text.Align // default horizontal align
+ HeaderVAlign text.VAlign // default vertical align
+ Row text.Format // default text format
+ RowAlign text.Align // default horizontal align
+ RowVAlign text.VAlign // default vertical align
+}
+
+var (
+ // FormatOptionsDefault defines sensible formatting options.
+ FormatOptionsDefault = FormatOptions{
+ Footer: text.FormatUpper,
+ FooterAlign: text.AlignDefault,
+ FooterVAlign: text.VAlignDefault,
+ Header: text.FormatUpper,
+ HeaderAlign: text.AlignDefault,
+ HeaderVAlign: text.VAlignDefault,
+ Row: text.FormatDefault,
+ RowAlign: text.AlignDefault,
+ RowVAlign: text.VAlignDefault,
+ }
+)
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/style_html.go b/vendor/github.com/jedib0t/go-pretty/v6/table/style_html.go
new file mode 100644
index 00000000..a91fc73a
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/style_html.go
@@ -0,0 +1,21 @@
+package table
+
+// HTMLOptions defines the global options to control HTML rendering.
+type HTMLOptions struct {
+ ConvertColorsToSpans bool // convert ANSI escape sequences to HTML tags with CSS classes? EscapeText will be true if this is true.
+ CSSClass string // CSS class to set on the overall tag
+ EmptyColumn string // string to replace "" columns with (entire content being "")
+ EscapeText bool // escape text into HTML-safe content?
+ Newline string // string to replace "\n" characters with
+}
+
+var (
+ // DefaultHTMLOptions defines sensible HTML rendering defaults.
+ DefaultHTMLOptions = HTMLOptions{
+ ConvertColorsToSpans: true,
+ CSSClass: DefaultHTMLCSSClass,
+ EmptyColumn: " ",
+ EscapeText: true,
+ Newline: "
",
+ }
+)
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/style_options.go b/vendor/github.com/jedib0t/go-pretty/v6/table/style_options.go
new file mode 100644
index 00000000..7e7a27c8
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/style_options.go
@@ -0,0 +1,109 @@
+package table
+
+// Options defines the global options that determine how the Table is
+// rendered.
+type Options struct {
+ // DoNotColorBordersAndSeparators disables coloring all the borders and row
+ // or column separators.
+ DoNotColorBordersAndSeparators bool
+
+ // DoNotRenderSeparatorWhenEmpty disables rendering the separator row after
+ // headers when there are no data rows (for example when only headers and/or
+ // footers are present).
+ DoNotRenderSeparatorWhenEmpty bool
+
+ // DrawBorder enables or disables drawing the border around the Table.
+ // Example of a table where it is disabled:
+ // # │ FIRST NAME │ LAST NAME │ SALARY │
+ // ─────┼────────────┼───────────┼────────┼─────────────────────────────
+ // 1 │ Arya │ Stark │ 3000 │
+ // 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow!
+ // 300 │ Tyrion │ Lannister │ 5000 │
+ // ─────┼────────────┼───────────┼────────┼─────────────────────────────
+ // │ │ TOTAL │ 10000 │
+ DrawBorder bool
+
+ // SeparateColumns enables or disable drawing border between columns.
+ // Example of a table where it is disabled:
+ // ┌─────────────────────────────────────────────────────────────────┐
+ // │ # FIRST NAME LAST NAME SALARY │
+ // ├─────────────────────────────────────────────────────────────────┤
+ // │ 1 Arya Stark 3000 │
+ // │ 20 Jon Snow 2000 You know nothing, Jon Snow! │
+ // │ 300 Tyrion Lannister 5000 │
+ // │ TOTAL 10000 │
+ // └─────────────────────────────────────────────────────────────────┘
+ SeparateColumns bool
+
+ // SeparateFooter enables or disable drawing border between the footer and
+ // the rows. Example of a table where it is disabled:
+ // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
+ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ 1 │ Arya │ Stark │ 3000 │ │
+ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+ // │ 300 │ Tyrion │ Lannister │ 5000 │ │
+ // │ │ │ TOTAL │ 10000 │ │
+ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
+ SeparateFooter bool
+
+ // SeparateHeader enables or disable drawing border between the header and
+ // the rows. Example of a table where it is disabled:
+ // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
+ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+ // │ 1 │ Arya │ Stark │ 3000 │ │
+ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+ // │ 300 │ Tyrion │ Lannister │ 5000 │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ │ │ TOTAL │ 10000 │ │
+ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
+ SeparateHeader bool
+
+ // SeparateRows enables or disables drawing separators between each row.
+ // Example of a table where it is enabled:
+ // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
+ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ 1 │ Arya │ Stark │ 3000 │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ 300 │ Tyrion │ Lannister │ 5000 │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ │ │ TOTAL │ 10000 │ │
+ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
+ SeparateRows bool
+}
+
+var (
+ // OptionsDefault defines sensible global options.
+ OptionsDefault = Options{
+ DoNotColorBordersAndSeparators: false,
+ DrawBorder: true,
+ SeparateColumns: true,
+ SeparateFooter: true,
+ SeparateHeader: true,
+ SeparateRows: false,
+ }
+
+ // OptionsNoBorders sets up a table without any borders.
+ OptionsNoBorders = Options{
+ DoNotColorBordersAndSeparators: false,
+ DrawBorder: false,
+ SeparateColumns: true,
+ SeparateFooter: true,
+ SeparateHeader: true,
+ SeparateRows: false,
+ }
+
+ // OptionsNoBordersAndSeparators sets up a table without any borders or
+ // separators.
+ OptionsNoBordersAndSeparators = Options{
+ DoNotColorBordersAndSeparators: false,
+ DrawBorder: false,
+ SeparateColumns: false,
+ SeparateFooter: false,
+ SeparateHeader: false,
+ SeparateRows: false,
+ }
+)
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/style_size.go b/vendor/github.com/jedib0t/go-pretty/v6/table/style_size.go
new file mode 100644
index 00000000..b3806948
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/style_size.go
@@ -0,0 +1,21 @@
+package table
+
+// SizeOptions defines the way to control the width of the table output.
+type SizeOptions struct {
+ // WidthMax is the maximum allotted width for the full row;
+ // any content beyond this will be truncated using the text
+ // in Style.Box.UnfinishedRow
+ WidthMax int
+ // WidthMin is the minimum allotted width for the full row;
+ // columns will be auto-expanded until the overall width
+ // is met
+ WidthMin int
+}
+
+var (
+ // SizeOptionsDefault defines sensible size options - basically NONE.
+ SizeOptionsDefault = SizeOptions{
+ WidthMax: 0,
+ WidthMin: 0,
+ }
+)
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/styles_title.go b/vendor/github.com/jedib0t/go-pretty/v6/table/styles_title.go
new file mode 100644
index 00000000..1a22a872
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/styles_title.go
@@ -0,0 +1,81 @@
+package table
+
+import "github.com/jedib0t/go-pretty/v6/text"
+
+// TitleOptions defines the way the title text is to be rendered.
+type TitleOptions struct {
+ Align text.Align
+ Colors text.Colors
+ Format text.Format
+}
+
+var (
+ // TitleOptionsDefault defines sensible title options - basically NONE.
+ TitleOptionsDefault = TitleOptions{}
+
+ // TitleOptionsBright renders Bright Bold text on Dark background.
+ TitleOptionsBright = TitleOptionsBlackOnCyan
+
+ // TitleOptionsDark renders Dark Bold text on Bright background.
+ TitleOptionsDark = TitleOptionsCyanOnBlack
+
+ // TitleOptionsBlackOnBlue renders Black text on Blue background.
+ TitleOptionsBlackOnBlue = TitleOptions{
+ Colors: append(ColorOptionsBlackOnBlueWhite.Header, text.Bold),
+ }
+
+ // TitleOptionsBlackOnCyan renders Black Bold text on Cyan background.
+ TitleOptionsBlackOnCyan = TitleOptions{
+ Colors: append(ColorOptionsBlackOnCyanWhite.Header, text.Bold),
+ }
+
+ // TitleOptionsBlackOnGreen renders Black Bold text onGreen background.
+ TitleOptionsBlackOnGreen = TitleOptions{
+ Colors: append(ColorOptionsBlackOnGreenWhite.Header, text.Bold),
+ }
+
+ // TitleOptionsBlackOnMagenta renders Black Bold text on Magenta background.
+ TitleOptionsBlackOnMagenta = TitleOptions{
+ Colors: append(ColorOptionsBlackOnMagentaWhite.Header, text.Bold),
+ }
+
+ // TitleOptionsBlackOnRed renders Black Bold text on Red background.
+ TitleOptionsBlackOnRed = TitleOptions{
+ Colors: append(ColorOptionsBlackOnRedWhite.Header, text.Bold),
+ }
+
+ // TitleOptionsBlackOnYellow renders Black Bold text on Yellow background.
+ TitleOptionsBlackOnYellow = TitleOptions{
+ Colors: append(ColorOptionsBlackOnYellowWhite.Header, text.Bold),
+ }
+
+ // TitleOptionsBlueOnBlack renders Blue Bold text on Black background.
+ TitleOptionsBlueOnBlack = TitleOptions{
+ Colors: append(ColorOptionsBlueWhiteOnBlack.Header, text.Bold),
+ }
+
+ // TitleOptionsCyanOnBlack renders Cyan Bold text on Black background.
+ TitleOptionsCyanOnBlack = TitleOptions{
+ Colors: append(ColorOptionsCyanWhiteOnBlack.Header, text.Bold),
+ }
+
+ // TitleOptionsGreenOnBlack renders Green Bold text on Black background.
+ TitleOptionsGreenOnBlack = TitleOptions{
+ Colors: append(ColorOptionsGreenWhiteOnBlack.Header, text.Bold),
+ }
+
+ // TitleOptionsMagentaOnBlack renders Magenta Bold text on Black background.
+ TitleOptionsMagentaOnBlack = TitleOptions{
+ Colors: append(ColorOptionsMagentaWhiteOnBlack.Header, text.Bold),
+ }
+
+ // TitleOptionsRedOnBlack renders Red Bold text on Black background.
+ TitleOptionsRedOnBlack = TitleOptions{
+ Colors: append(ColorOptionsRedWhiteOnBlack.Header, text.Bold),
+ }
+
+ // TitleOptionsYellowOnBlack renders Yellow Bold text on Black background.
+ TitleOptionsYellowOnBlack = TitleOptions{
+ Colors: append(ColorOptionsYellowWhiteOnBlack.Header, text.Bold),
+ }
+)
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/table.go b/vendor/github.com/jedib0t/go-pretty/v6/table/table.go
index 191dd771..02d40926 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/table/table.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/table.go
@@ -28,6 +28,8 @@ type Table struct {
// columnConfigMap stores the custom-configuration by column
// number and is generated before rendering
columnConfigMap map[int]ColumnConfig
+ // directionModifier caches the direction modifier string to avoid repeated calls
+ directionModifier string
// firstRowOfPage tells if the renderer is on the first row of a page?
firstRowOfPage bool
// htmlCSSClass stores the HTML CSS Class to use on the node
@@ -50,6 +52,8 @@ type Table struct {
outputMirror io.Writer
// pager controls how the output is separated into pages
pager pager
+ // renderMode contains the type of table to render
+ renderMode renderMode
// rows stores the rows that make up the body (in string form)
rows []rowStr
// rowsColors stores the text.Colors over-rides for each row as defined by
@@ -59,6 +63,8 @@ type Table struct {
rowsConfigMap map[int]RowConfig
// rowsRaw stores the rows that make up the body
rowsRaw []Row
+ // rowsRawFiltered is the filtered version of rowsRaw
+ rowsRawFiltered []Row
// rowsFooter stores the rows that make up the footer (in string form)
rowsFooter []rowStr
// rowsFooterConfigs stores RowConfig for each footer row
@@ -76,9 +82,11 @@ type Table struct {
rowPainter RowPainter
// rowPainterWithAttributes is same as rowPainter, but with attributes
rowPainterWithAttributes RowPainterWithAttributes
- // rowSeparator is a dummy row that contains the separator columns (dashes
- // that make up the separator between header/body/footer
- rowSeparator rowStr
+ // rowSeparators contains the separator columns (dashes that make up the
+ // separators between title/header/body/footer
+ rowSeparators map[string]rowStr
+ // rowSeparatorStrings contains the separator strings for each separator type
+ rowSeparatorStrings map[separatorType]string
// separators is used to keep track of all rowIndices after which a
// separator has to be rendered
separators map[int]bool
@@ -86,6 +94,8 @@ type Table struct {
sortBy []SortBy
// sortedRowIndices is the output of sorting
sortedRowIndices []int
+ // filterBy stores the filter criteria
+ filterBy []FilterBy
// style contains all the strings used to draw the table, and more
style *Style
// suppressEmptyColumns hides columns which have no content on all regular
@@ -127,12 +137,16 @@ func (t *Table) AppendHeader(row Row, config ...RowConfig) {
//
// Only the first item in the "config" will be tagged against this row.
func (t *Table) AppendRow(row Row, config ...RowConfig) {
- t.rowsRaw = append(t.rowsRaw, row)
+ t.rowsRawFiltered = append(t.rowsRawFiltered, row)
+ // Keep original rows in sync for filtering
+ rowCopy := make(Row, len(row))
+ copy(rowCopy, row)
+ t.rowsRaw = append(t.rowsRaw, rowCopy)
if len(config) > 0 {
if t.rowsConfigMap == nil {
t.rowsConfigMap = make(map[int]RowConfig)
}
- t.rowsConfigMap[len(t.rowsRaw)-1] = config[0]
+ t.rowsConfigMap[len(t.rowsRawFiltered)-1] = config[0]
}
}
@@ -164,11 +178,17 @@ func (t *Table) AppendSeparator() {
if t.separators == nil {
t.separators = make(map[int]bool)
}
- if len(t.rowsRaw) > 0 {
- t.separators[len(t.rowsRaw)-1] = true
+ if len(t.rowsRawFiltered) > 0 {
+ t.separators[len(t.rowsRawFiltered)-1] = true
}
}
+// FilterBy sets the rules for filtering the Rows. All filters are applied with
+// AND logic (all must match). Filters are applied before sorting.
+func (t *Table) FilterBy(filterBy []FilterBy) {
+ t.filterBy = filterBy
+}
+
// ImportGrid helps import 1d or 2d arrays as rows.
func (t *Table) ImportGrid(grid interface{}) bool {
rows := objAsSlice(grid)
@@ -190,7 +210,7 @@ func (t *Table) ImportGrid(grid interface{}) bool {
// Length returns the number of rows to be rendered.
func (t *Table) Length() int {
- return len(t.rowsRaw)
+ return len(t.rowsRawFiltered)
}
// Pager returns an object that splits the table output into pages and
@@ -231,6 +251,7 @@ func (t *Table) ResetHeaders() {
// ResetRows resets and clears all the rows appended earlier.
func (t *Table) ResetRows() {
+ t.rowsRawFiltered = nil
t.rowsRaw = nil
t.separators = nil
}
@@ -305,12 +326,12 @@ func (t *Table) SetRowPainter(painter interface{}) {
t.rowPainterWithAttributes = nil
// if called as SetRowPainter(RowPainter(func...))
- switch painter.(type) {
+ switch p := painter.(type) {
case RowPainter:
- t.rowPainter = painter.(RowPainter)
+ t.rowPainter = p
return
case RowPainterWithAttributes:
- t.rowPainterWithAttributes = painter.(RowPainterWithAttributes)
+ t.rowPainterWithAttributes = p
return
}
@@ -367,6 +388,31 @@ func (t *Table) SuppressTrailingSpaces() {
t.suppressTrailingSpaces = true
}
+// calculateNumColumnsFromRaw calculates the number of columns from raw rows and headers
+func (t *Table) calculateNumColumnsFromRaw() {
+ t.numColumns = 0
+ // Check headers first
+ if len(t.rowsHeaderRaw) > 0 {
+ for _, headerRow := range t.rowsHeaderRaw {
+ if len(headerRow) > t.numColumns {
+ t.numColumns = len(headerRow)
+ }
+ }
+ }
+ // Check data rows
+ for _, row := range t.rowsRawFiltered {
+ if len(row) > t.numColumns {
+ t.numColumns = len(row)
+ }
+ }
+ // Check footer rows
+ for _, footerRow := range t.rowsFooterRaw {
+ if len(footerRow) > t.numColumns {
+ t.numColumns = len(footerRow)
+ }
+ }
+}
+
func (t *Table) getAlign(colIdx int, hint renderHint) text.Align {
align := text.AlignDefault
if cfg, ok := t.columnConfigMap[colIdx]; ok {
@@ -505,13 +551,13 @@ func (t *Table) getColumnSeparator(row rowStr, colIdx int, hint renderHint) stri
if hint.isSeparatorRow {
if hint.isBorderTop {
if t.shouldMergeCellsHorizontallyBelow(row, colIdx, hint) {
- separator = t.style.Box.MiddleHorizontal
+ separator = t.style.Box.middleHorizontal(hint.separatorType)
} else {
separator = t.style.Box.TopSeparator
}
} else if hint.isBorderBottom {
if t.shouldMergeCellsHorizontallyAbove(row, colIdx, hint) {
- separator = t.style.Box.MiddleHorizontal
+ separator = t.style.Box.middleHorizontal(hint.separatorType)
} else {
separator = t.style.Box.BottomSeparator
}
@@ -531,7 +577,7 @@ func (t *Table) getColumnSeparatorNonBorder(mergeCellsAbove bool, mergeCellsBelo
}
mergeCurrCol := t.shouldMergeCellsVerticallyAbove(colIdx-1, hint)
- return t.getColumnSeparatorNonBorderNonAutoIndex(mergeCellsAbove, mergeCellsBelow, mergeCurrCol, mergeNextCol)
+ return t.getColumnSeparatorNonBorderNonAutoIndex(mergeCellsAbove, mergeCellsBelow, mergeCurrCol, mergeNextCol, hint)
}
func (t *Table) getColumnSeparatorNonBorderAutoIndex(mergeNextCol bool, hint renderHint) string {
@@ -546,11 +592,11 @@ func (t *Table) getColumnSeparatorNonBorderAutoIndex(mergeNextCol bool, hint ren
return t.style.Box.MiddleSeparator
}
-func (t *Table) getColumnSeparatorNonBorderNonAutoIndex(mergeCellsAbove bool, mergeCellsBelow bool, mergeCurrCol bool, mergeNextCol bool) string {
+func (t *Table) getColumnSeparatorNonBorderNonAutoIndex(mergeCellsAbove bool, mergeCellsBelow bool, mergeCurrCol bool, mergeNextCol bool, hint renderHint) string {
if mergeCellsAbove && mergeCellsBelow && mergeCurrCol && mergeNextCol {
return t.style.Box.EmptySeparator
} else if mergeCellsAbove && mergeCellsBelow {
- return t.style.Box.MiddleHorizontal
+ return t.style.Box.middleHorizontal(hint.separatorType)
} else if mergeCellsAbove {
return t.style.Box.TopSeparator
} else if mergeCellsBelow {
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/util.go b/vendor/github.com/jedib0t/go-pretty/v6/table/util.go
index 4636e881..b4179a20 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/table/util.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/util.go
@@ -1,8 +1,10 @@
package table
import (
+ "fmt"
"reflect"
"sort"
+ "strconv"
)
// AutoIndexColumnID returns a unique Column ID/Name for the given Column Number.
@@ -26,6 +28,48 @@ func widthEnforcerNone(col string, _ int) string {
return col
}
+// convertValueToString converts a value to string using fast type assertions
+// for common numeric types before falling back to fmt.Sprint.
+//
+//gocyclo:ignore
+func convertValueToString(v interface{}) string {
+ switch val := v.(type) {
+ case int:
+ return strconv.FormatInt(int64(val), 10)
+ case int8:
+ return strconv.FormatInt(int64(val), 10)
+ case int16:
+ return strconv.FormatInt(int64(val), 10)
+ case int32:
+ return strconv.FormatInt(int64(val), 10)
+ case int64:
+ return strconv.FormatInt(val, 10)
+ case uint:
+ return strconv.FormatUint(uint64(val), 10)
+ case uint8:
+ return strconv.FormatUint(uint64(val), 10)
+ case uint16:
+ return strconv.FormatUint(uint64(val), 10)
+ case uint32:
+ return strconv.FormatUint(uint64(val), 10)
+ case uint64:
+ return strconv.FormatUint(val, 10)
+ case float32:
+ return strconv.FormatFloat(float64(val), 'g', -1, 32)
+ case float64:
+ return strconv.FormatFloat(val, 'g', -1, 64)
+ case bool:
+ if val {
+ return "true"
+ }
+ return "false"
+ case string:
+ return val
+ default:
+ return fmt.Sprint(v)
+ }
+}
+
// isNumber returns true if the argument is a numeric type; false otherwise.
func isNumber(x interface{}) bool {
if x == nil {
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/util_html.go b/vendor/github.com/jedib0t/go-pretty/v6/table/util_html.go
new file mode 100644
index 00000000..97e2c521
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/util_html.go
@@ -0,0 +1,151 @@
+package table
+
+import (
+ "strings"
+
+ "github.com/jedib0t/go-pretty/v6/text"
+)
+
+// convertEscSequencesToSpans converts ANSI escape sequences to HTML tags with CSS classes.
+func convertEscSequencesToSpans(str string) string {
+ converter := newEscSeqToSpanConverter()
+ return converter.Convert(str)
+}
+
+// escSeqToSpanConverter converts ANSI escape sequences to HTML tags with CSS classes.
+type escSeqToSpanConverter struct {
+ result strings.Builder
+ esp text.EscSeqParser
+ currentColors map[int]bool
+}
+
+// newEscSeqToSpanConverter creates a new escape sequence to span converter.
+func newEscSeqToSpanConverter() *escSeqToSpanConverter {
+ return &escSeqToSpanConverter{
+ currentColors: make(map[int]bool),
+ }
+}
+
+// Convert converts ANSI escape sequences in the string to HTML tags with CSS classes.
+func (c *escSeqToSpanConverter) Convert(str string) string {
+ c.reset()
+
+ // Process the string character by character
+ for _, char := range str {
+ wasInSequence := c.esp.InSequence()
+ c.esp.Consume(char)
+
+ if c.esp.InSequence() {
+ // We're inside an escape sequence, skip it (don't write to result)
+ continue
+ }
+
+ if wasInSequence {
+ // We just finished an escape sequence, update colors
+ newColors := make(map[int]bool)
+ for _, code := range c.esp.Codes() {
+ newColors[code] = true
+ }
+ c.updateSpan(newColors)
+ } else {
+ // Regular character, escape it for HTML safety and write it
+ // (will be inside current span if colors are active)
+ c.writeEscapedRune(char)
+ }
+ }
+
+ // Close any open span
+ if len(c.currentColors) > 0 {
+ c.result.WriteString("")
+ }
+
+ return c.result.String()
+}
+
+// clearColors clears the current color tracking.
+func (c *escSeqToSpanConverter) clearColors() {
+ c.currentColors = make(map[int]bool)
+}
+
+// closeSpan closes the current span if one is open.
+func (c *escSeqToSpanConverter) closeSpan() {
+ if len(c.currentColors) > 0 {
+ c.result.WriteString("")
+ }
+}
+
+// colorsChanged checks if the color set has changed.
+func (c *escSeqToSpanConverter) colorsChanged(newColors map[int]bool) bool {
+ // we never set the map values to false, so a simple size compare is enough
+ return len(c.currentColors) != len(newColors)
+}
+
+// cssClasses converts color codes to CSS class names.
+func (c *escSeqToSpanConverter) cssClasses(codes map[int]bool) string {
+ var colors text.Colors
+ for code := range codes {
+ colors = append(colors, text.Color(code))
+ }
+ return colors.CSSClasses()
+}
+
+// openSpan opens a new span with the given CSS class and tracks the colors.
+func (c *escSeqToSpanConverter) openSpan(class string, newColors map[int]bool) {
+ c.result.WriteString("")
+ // Track colors since we opened a span
+ c.currentColors = make(map[int]bool)
+ for code := range newColors {
+ c.currentColors[code] = true
+ }
+}
+
+// reset initializes the converter state for a new conversion.
+func (c *escSeqToSpanConverter) reset() {
+ c.result.Reset()
+ c.esp = text.EscSeqParser{}
+ c.currentColors = make(map[int]bool)
+}
+
+// updateSpan updates span tags when colors change.
+func (c *escSeqToSpanConverter) updateSpan(newColors map[int]bool) {
+ if !c.colorsChanged(newColors) {
+ return
+ }
+
+ c.closeSpan()
+
+ // Open new span if there are colors with valid CSS classes
+ if len(newColors) > 0 {
+ class := c.cssClasses(newColors)
+ if class != "" {
+ c.openSpan(class, newColors)
+ } else {
+ // No CSS classes, so don't track these colors
+ c.clearColors()
+ }
+ } else {
+ // No colors, clear tracking
+ c.clearColors()
+ }
+}
+
+// writeEscapedRune writes a rune to the result, escaping it if necessary for HTML safety.
+func (c *escSeqToSpanConverter) writeEscapedRune(char rune) {
+ switch char {
+ case '<':
+ c.result.WriteString("<")
+ case '>':
+ c.result.WriteString(">")
+ case '&':
+ c.result.WriteString("&")
+ case '"':
+ c.result.WriteString(""")
+ case '\'':
+ c.result.WriteString("'")
+ default:
+ // Most characters don't need escaping, write directly
+ c.result.WriteRune(char)
+ }
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/writer.go b/vendor/github.com/jedib0t/go-pretty/v6/table/writer.go
index 51eee8c7..15a96cf4 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/table/writer.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/writer.go
@@ -11,6 +11,7 @@ type Writer interface {
AppendRow(row Row, configs ...RowConfig)
AppendRows(rows []Row, configs ...RowConfig)
AppendSeparator()
+ FilterBy(filterBy []FilterBy)
ImportGrid(grid interface{}) bool
Length() int
Pager(opts ...PagerOption) Pager
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/README.md b/vendor/github.com/jedib0t/go-pretty/v6/text/README.md
index afd163af..0e8308a4 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/text/README.md
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/README.md
@@ -1,8 +1,141 @@
-# text
+# Text
+[](https://pkg.go.dev/github.com/jedib0t/go-pretty/v6/text)
-[](https://pkg.go.dev/github.com/jedib0t/go-pretty/v6/text)
-
-Package with utility functions to manipulate strings/text.
+Package with utility functions to manipulate strings/text with full support for
+ANSI escape sequences (colors, formatting, etc.).
Used heavily in the other packages in this repo ([list](../list),
-[progress](../progress), and [table](../table)).
\ No newline at end of file
+[progress](../progress), and [table](../table)).
+
+## Features
+
+### Colors & Formatting
+
+ - **ANSI Color Support** - Full support for terminal colors and formatting
+ - Foreground colors (Black, Red, Green, Yellow, Blue, Magenta, Cyan, White)
+ - Background colors (matching foreground set)
+ - Hi-intensity variants for both foreground and background
+ - **256-color palette support** - Extended color support for terminals
+ - Standard 16 colors (0-15)
+ - RGB cube colors (16-231) - 216 colors organized in a 6x6x6 cube
+ - Grayscale colors (232-255) - 24 shades of gray
+ - Helper functions: `Fg256Color(index)`, `Bg256Color(index)`, `Fg256RGB(r, g, b)`, `Bg256RGB(r, g, b)`
+ - Text attributes (Bold, Faint, Italic, Underline, Blink, Reverse, Concealed, CrossedOut)
+ - Automatic color detection based on environment variables (`NO_COLOR`, `FORCE_COLOR`, `TERM`)
+ - Global enable/disable functions for colors
+ - Cached escape sequences for performance
+ - **Text Formatting** - Transform text while preserving escape sequences
+ - `FormatDefault` - No transformation
+ - `FormatLower` - Convert to lowercase
+ - `FormatTitle` - Convert to title case
+ - `FormatUpper` - Convert to uppercase
+ - **HTML Support** - Generate HTML class attributes for colors
+ - **Color Combinations** - Combine multiple colors and attributes
+
+### Alignment
+
+ - **Horizontal Alignment**
+ - `AlignDefault` / `AlignLeft` - Left-align text
+ - `AlignCenter` - Center-align text
+ - `AlignRight` - Right-align text
+ - `AlignJustify` - Justify text (distribute spaces between words)
+ - `AlignAuto` - Auto-detect: right-align numbers, left-align text
+ - HTML and Markdown property generation for alignment
+ - **Vertical Alignment**
+ - `VAlignTop` - Align to top
+ - `VAlignMiddle` - Align to middle
+ - `VAlignBottom` - Align to bottom
+ - Works with both string arrays and multi-line strings
+ - HTML property generation for vertical alignment
+
+### Text Wrapping
+
+ - **WrapHard** - Hard wrap at specified length, breaks words if needed
+ - Handles ANSI escape sequences without breaking formatting
+ - Preserves paragraph breaks
+ - **WrapSoft** - Soft wrap at specified length, tries to keep words intact
+ - Handles ANSI escape sequences without breaking formatting
+ - Preserves paragraph breaks
+ - **WrapText** - Similar to WrapHard but also respects line breaks
+ - Handles ANSI escape sequences without breaking formatting
+
+### String Utilities
+
+ - **Width Calculation**
+ - `StringWidth` - Calculate display width of string (including escape sequences)
+ - `StringWidthWithoutEscSequences` - Calculate display width ignoring escape sequences
+ - `RuneWidth` - Calculate display width of a single rune (handles East Asian characters)
+ - `LongestLineLen` - Find the longest line in a multi-line string
+ - **String Manipulation**
+ - `Trim` - Trim string to specified length while preserving escape sequences
+ - `Pad` - Pad string to specified length with a character
+ - `Snip` - Snip string to specified length with an indicator (e.g., "~")
+ - `RepeatAndTrim` - Repeat string until it reaches specified length
+ - `InsertEveryN` - Insert a character every N characters
+ - `ProcessCRLF` - Process carriage returns and line feeds correctly
+ - `Widen` - Convert half-width characters to full-width
+ - **Escape Sequence Handling**
+ - All functions properly handle ANSI escape sequences
+ - Escape sequences are preserved during transformations
+ - Width calculations ignore escape sequences
+ - `EscSeqParser` - Parser for advanced escape sequence parsing and tracking
+ - Supports both CSI (Control Sequence Introducer) and OSI (Operating System Command) formats
+ - Tracks active formatting codes and can generate consolidated escape sequences
+ - Full support for 256-color escape sequences (`\x1b[38;5;n`m` and `\x1b[48;5;n`m`)
+
+### Cursor Control
+
+ - Move cursor in all directions
+ - `CursorUp` - Move cursor up N lines
+ - `CursorDown` - Move cursor down N lines
+ - `CursorLeft` - Move cursor left N characters
+ - `CursorRight` - Move cursor right N characters
+ - `EraseLine` - Erase all characters to the right of cursor
+ - Generate ANSI escape sequences for terminal cursor manipulation
+
+### Hyperlinks
+
+ - **Terminal Hyperlinks** - Create clickable hyperlinks in supported terminals
+ - Uses OSC 8 escape sequences
+ - Format: `Hyperlink(url, text)`
+ - Falls back to plain text in unsupported terminals
+
+### Transformers
+
+ - **Number Transformer** - Format numbers with colors
+ - Positive numbers colored green
+ - Negative numbers colored red
+ - Custom format string support (e.g., `%.2f`)
+ - Supports all numeric types (int, uint, float)
+ - **JSON Transformer** - Pretty-print JSON strings or objects
+ - Customizable indentation (prefix and indent string)
+ - Validates JSON before formatting
+ - **Time Transformer** - Format time.Time objects
+ - Custom layout support (e.g., `time.RFC3339`)
+ - Timezone localization support
+ - Auto-detects common time formats from strings
+ - **Unix Time Transformer** - Format Unix timestamps
+ - Handles seconds, milliseconds, microseconds, and nanoseconds
+ - Auto-detects timestamp unit based on value
+ - Timezone localization support
+ - **URL Transformer** - Format URLs with styling
+ - Underlined and colored blue by default
+ - Custom color support
+
+### Text Direction
+
+ - **Bidirectional Text Support**
+ - `LeftToRight` - Force left-to-right text direction
+ - `RightToLeft` - Force right-to-left text direction
+ - Uses Unicode directional markers
+
+### Filtering
+
+ - **String Filtering** - Filter string slices with custom functions
+ - `Filter(slice, predicate)` - Returns filtered slice
+
+### East Asian Character Support
+
+ - Proper width calculation for East Asian characters (full-width, half-width)
+ - Configurable East Asian width handling via `OverrideRuneWidthEastAsianWidth()`
+ - Handles mixed character sets correctly
\ No newline at end of file
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/ansi_unix.go b/vendor/github.com/jedib0t/go-pretty/v6/text/ansi_unix.go
index 635be79e..e873a7b6 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/text/ansi_unix.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/ansi_unix.go
@@ -3,6 +3,12 @@
package text
+import "os"
+
func areANSICodesSupported() bool {
- return true
+ // On Unix systems, ANSI codes are generally supported unless TERM is "dumb"
+ // This is a basic check; 256-color sequences are ANSI sequences and will
+ // be handled by terminals that support them (or ignored by those that don't)
+ term := os.Getenv("TERM")
+ return term != "dumb"
}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/color.go b/vendor/github.com/jedib0t/go-pretty/v6/text/color.go
index 4154111f..32f6e5e2 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/text/color.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/color.go
@@ -9,6 +9,7 @@ import (
"sync"
)
+// colorsEnabled is true if colors are enabled and supported by the terminal.
var colorsEnabled = areColorsOnInTheEnv() && areANSICodesSupported()
// DisableColors (forcefully) disables color coding globally.
@@ -21,13 +22,24 @@ func EnableColors() {
colorsEnabled = true
}
-// areColorsOnInTheEnv returns true is colors are not disable using
+// areColorsOnInTheEnv returns true if colors are not disabled using
// well known environment variables.
func areColorsOnInTheEnv() bool {
- if os.Getenv("FORCE_COLOR") == "1" {
+ // FORCE_COLOR takes precedence - if set to a truthy value, enable colors
+ forceColor := os.Getenv("FORCE_COLOR")
+ if forceColor != "" && forceColor != "0" && forceColor != "false" {
return true
}
- return os.Getenv("NO_COLOR") == "" || os.Getenv("NO_COLOR") == "0"
+
+ // NO_COLOR: if set to any non-empty value (except "0"), disable colors
+ // Note: "0" is treated as "not set" to allow explicit enabling via NO_COLOR=0
+ noColor := os.Getenv("NO_COLOR")
+ if noColor != "" && noColor != "0" {
+ return false
+ }
+
+ // Default: check TERM - if not "dumb", assume colors are supported
+ return os.Getenv("TERM") != "dumb"
}
// The logic here is inspired from github.com/fatih/color; the following is
@@ -103,18 +115,62 @@ const (
BgHiWhite
)
+// 256-color support
+// Internal encoding for 256-color codes (used by escape_seq_parser.go):
+// Foreground 256-color: fg256Start + colorIndex (1000-1255)
+// Background 256-color: bg256Start + colorIndex (2000-2255)
+const (
+ // fg256Start is the base value for 256-color foreground colors.
+ // Use Fg256Color(index) to create a 256-color foreground color.
+ fg256Start Color = 1000
+ // bg256Start is the base value for 256-color background colors.
+ // Use Bg256Color(index) to create a 256-color background color.
+ bg256Start Color = 2000
+)
+
+// CSSClasses returns the CSS class names for the color.
+func (c Color) CSSClasses() string {
+ // Check for 256-color and convert to RGB-based class
+ if c >= fg256Start && c < fg256Start+256 {
+ colorIndex := int(c - fg256Start)
+ r, g, b := color256ToRGB(colorIndex)
+ return fmt.Sprintf("fg-256-%d-%d-%d", r, g, b)
+ }
+ if c >= bg256Start && c < bg256Start+256 {
+ colorIndex := int(c - bg256Start)
+ r, g, b := color256ToRGB(colorIndex)
+ return fmt.Sprintf("bg-256-%d-%d-%d", r, g, b)
+ }
+ // Existing behavior for standard colors
+ if class, ok := colorCSSClassMap[c]; ok {
+ return class
+ }
+ return ""
+}
+
// EscapeSeq returns the ANSI escape sequence for the color.
func (c Color) EscapeSeq() string {
+ // Check if it's a 256-color foreground (1000-1255)
+ if c >= fg256Start && c < fg256Start+256 {
+ colorIndex := int(c - fg256Start)
+ return fmt.Sprintf("%s38;5;%d%s", EscapeStart, colorIndex, EscapeStop)
+ }
+ // Check if it's a 256-color background (2000-2255)
+ if c >= bg256Start && c < bg256Start+256 {
+ colorIndex := int(c - bg256Start)
+ return fmt.Sprintf("%s48;5;%d%s", EscapeStart, colorIndex, EscapeStop)
+ }
+ // Regular color (existing behavior)
return EscapeStart + strconv.Itoa(int(c)) + EscapeStop
}
// HTMLProperty returns the "class" attribute for the color.
func (c Color) HTMLProperty() string {
- out := ""
- if class, ok := colorCSSClassMap[c]; ok {
- out = fmt.Sprintf("class=\"%s\"", class)
+ classes := c.CSSClasses()
+ if classes == "" {
+ return ""
}
- return out
+ return fmt.Sprintf("class=\"%s\"", classes)
}
// Sprint colorizes and prints the given string(s).
@@ -134,6 +190,25 @@ type Colors []Color
// colorsSeqMap caches the escape sequence for a set of colors
var colorsSeqMap = sync.Map{}
+// CSSClasses returns the CSS class names for the colors.
+func (c Colors) CSSClasses() string {
+ if len(c) == 0 {
+ return ""
+ }
+
+ var classes []string
+ for _, color := range c {
+ class := color.CSSClasses()
+ if class != "" {
+ classes = append(classes, class)
+ }
+ }
+ if len(classes) > 1 {
+ sort.Strings(classes)
+ }
+ return strings.Join(classes, " ")
+}
+
// EscapeSeq returns the ANSI escape sequence for the colors set.
func (c Colors) EscapeSeq() string {
if len(c) == 0 {
@@ -143,32 +218,39 @@ func (c Colors) EscapeSeq() string {
colorsKey := fmt.Sprintf("%#v", c)
escapeSeq, ok := colorsSeqMap.Load(colorsKey)
if !ok || escapeSeq == "" {
- colorNums := make([]string, len(c))
- for idx, color := range c {
- colorNums[idx] = strconv.Itoa(int(color))
+ codes := make([]string, 0, len(c))
+ for _, color := range c {
+ codes = append(codes, c.colorToCode(color))
}
- escapeSeq = EscapeStart + strings.Join(colorNums, ";") + EscapeStop
+ escapeSeq = EscapeStart + strings.Join(codes, ";") + EscapeStop
colorsSeqMap.Store(colorsKey, escapeSeq)
}
return escapeSeq.(string)
}
+// colorToCode converts a Color to its escape sequence code string.
+func (c Colors) colorToCode(color Color) string {
+ // Check if it's a 256-color foreground (1000-1255)
+ if color >= fg256Start && color < fg256Start+256 {
+ colorIndex := int(color - fg256Start)
+ return fmt.Sprintf("38;5;%d", colorIndex)
+ }
+ // Check if it's a 256-color background (2000-2255)
+ if color >= bg256Start && color < bg256Start+256 {
+ colorIndex := int(color - bg256Start)
+ return fmt.Sprintf("48;5;%d", colorIndex)
+ }
+ // Regular color
+ return strconv.Itoa(int(color))
+}
+
// HTMLProperty returns the "class" attribute for the colors.
func (c Colors) HTMLProperty() string {
- if len(c) == 0 {
+ classes := c.CSSClasses()
+ if classes == "" {
return ""
}
-
- var classes []string
- for _, color := range c {
- if class, ok := colorCSSClassMap[color]; ok {
- classes = append(classes, class)
- }
- }
- if len(classes) > 1 {
- sort.Strings(classes)
- }
- return fmt.Sprintf("class=\"%s\"", strings.Join(classes, " "))
+ return fmt.Sprintf("class=\"%s\"", classes)
}
// Sprint colorizes and prints the given string(s).
@@ -187,3 +269,81 @@ func colorize(s string, escapeSeq string) string {
}
return Escape(s, escapeSeq)
}
+
+// Fg256Color returns a foreground 256-color Color value.
+// The index must be in the range 0-255.
+func Fg256Color(index int) Color {
+ if index < 0 || index > 255 {
+ return Reset
+ }
+ return fg256Start + Color(index)
+}
+
+// Bg256Color returns a background 256-color Color value.
+// The index must be in the range 0-255.
+func Bg256Color(index int) Color {
+ if index < 0 || index > 255 {
+ return Reset
+ }
+ return bg256Start + Color(index)
+}
+
+// Fg256RGB returns a foreground 256-color from RGB values in the 6x6x6 color cube.
+// Each RGB component must be in the range 0-5.
+// The resulting color index will be in the range 16-231.
+func Fg256RGB(r, g, b int) Color {
+ if r < 0 || r > 5 || g < 0 || g > 5 || b < 0 || b > 5 {
+ return Reset
+ }
+ index := 16 + (r*36 + g*6 + b)
+ return Fg256Color(index)
+}
+
+// Bg256RGB returns a background 256-color from RGB values in the 6x6x6 color cube.
+// Each RGB component must be in the range 0-5.
+// The resulting color index will be in the range 16-231.
+func Bg256RGB(r, g, b int) Color {
+ if r < 0 || r > 5 || g < 0 || g > 5 || b < 0 || b > 5 {
+ return Reset
+ }
+ index := 16 + (r*36 + g*6 + b)
+ return Bg256Color(index)
+}
+
+// color256ToRGB converts a 256-color index to RGB values.
+// Returns (r, g, b) values in the range 0-255.
+func color256ToRGB(index int) (r, g, b int) {
+ if index < 16 {
+ // Standard 16 colors - map to predefined RGB values
+ standardColors := [16][3]int{
+ {0, 0, 0}, // 0: black
+ {128, 0, 0}, // 1: red
+ {0, 128, 0}, // 2: green
+ {128, 128, 0}, // 3: yellow
+ {0, 0, 128}, // 4: blue
+ {128, 0, 128}, // 5: magenta
+ {0, 128, 128}, // 6: cyan
+ {192, 192, 192}, // 7: light gray
+ {128, 128, 128}, // 8: dark gray
+ {255, 0, 0}, // 9: bright red
+ {0, 255, 0}, // 10: bright green
+ {255, 255, 0}, // 11: bright yellow
+ {0, 0, 255}, // 12: bright blue
+ {255, 0, 255}, // 13: bright magenta
+ {0, 255, 255}, // 14: bright cyan
+ {255, 255, 255}, // 15: white
+ }
+ return standardColors[index][0], standardColors[index][1], standardColors[index][2]
+ } else if index < 232 {
+ // 216-color RGB cube (16-231)
+ index -= 16
+ r = (index / 36) * 51
+ g = ((index / 6) % 6) * 51
+ b = (index % 6) * 51
+ } else {
+ // 24 grayscale colors (232-255)
+ gray := 8 + (index-232)*10
+ r, g, b = gray, gray, gray
+ }
+ return
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/escape_seq_parser.go b/vendor/github.com/jedib0t/go-pretty/v6/text/escape_seq_parser.go
index c6ffa437..66edae69 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/text/escape_seq_parser.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/escape_seq_parser.go
@@ -42,73 +42,7 @@ const (
escSeqKindOSI
)
-type escSeqParser struct {
- codes map[int]bool
-
- // consume specific
- inEscSeq bool
- escSeqKind escSeqKind
- escapeSeq string
-}
-
-func (s *escSeqParser) Codes() []int {
- codes := make([]int, 0)
- for code, val := range s.codes {
- if val {
- codes = append(codes, code)
- }
- }
- sort.Ints(codes)
- return codes
-}
-
-func (s *escSeqParser) Consume(char rune) {
- if !s.inEscSeq && char == EscapeStartRune {
- s.inEscSeq = true
- s.escSeqKind = escSeqKindUnknown
- s.escapeSeq = ""
- } else if s.inEscSeq && s.escSeqKind == escSeqKindUnknown {
- if char == EscapeStartRuneCSI {
- s.escSeqKind = escSeqKindCSI
- } else if char == EscapeStartRuneOSI {
- s.escSeqKind = escSeqKindOSI
- }
- }
-
- if s.inEscSeq {
- s.escapeSeq += string(char)
-
- // --- FIX for OSC 8 hyperlinks (e.g. \x1b]8;;url\x07label\x1b]8;;\x07)
- if s.escSeqKind == escSeqKindOSI &&
- strings.HasPrefix(s.escapeSeq, escapeStartConcealOSI) &&
- char == '\a' { // BEL
-
- s.ParseSeq(s.escapeSeq, s.escSeqKind)
- s.Reset()
- return
- }
-
- if s.isEscapeStopRune(char) {
- s.ParseSeq(s.escapeSeq, s.escSeqKind)
- s.Reset()
- }
- }
-}
-
-func (s *escSeqParser) InSequence() bool {
- return s.inEscSeq
-}
-
-func (s *escSeqParser) IsOpen() bool {
- return len(s.codes) > 0
-}
-
-func (s *escSeqParser) Reset() {
- s.inEscSeq = false
- s.escSeqKind = escSeqKindUnknown
- s.escapeSeq = ""
-}
-
+// private constants
const (
escCodeResetAll = 0
escCodeResetIntensity = 22
@@ -126,50 +60,132 @@ const (
escCodeReverse = 7
escCodeConceal = 8
escCodeCrossedOut = 9
+
+ // conceal OSI sequences
+ escapeStartConcealOSI = "\x1b]8;"
+ escapeStopConcealOSI = "\x1b\\"
)
-func (s *escSeqParser) ParseSeq(seq string, seqKind escSeqKind) {
- if s.codes == nil {
- s.codes = make(map[int]bool)
+// 256-color codes
+const (
+ escCode256FgStart = 38
+ escCode256BgStart = 48
+ escCode256Color = 5
+ escCodeResetFg = 39
+ escCodeResetBg = 49
+ escCode256Max = 255
+)
+
+// Internal encoding for 256-color codes uses fg256Start and bg256Start from color.go
+// Private constants initialized from private constants to avoid repeated casting in hot paths
+// Foreground 256-color: fg256Start + colorIndex (1000-1255)
+// Background 256-color: bg256Start + colorIndex (2000-2255)
+const (
+ escCode256FgBase = int(fg256Start) // 1000
+ escCode256BgBase = int(bg256Start) // 2000
+)
+
+// Standard color code ranges
+const (
+ // Standard foreground colors (30-37)
+ escCodeFgStdStart = 30
+ escCodeFgStdEnd = 37
+ // Bright foreground colors (90-97)
+ escCodeFgBrightStart = 90
+ escCodeFgBrightEnd = 97
+ // Standard background colors (40-47)
+ escCodeBgStdStart = 40
+ escCodeBgStdEnd = 47
+ // Bright background colors (100-107)
+ escCodeBgBrightStart = 100
+ escCodeBgBrightEnd = 107
+)
+
+// Special characters
+const (
+ escRuneBEL = '\a' // BEL character (ASCII 7)
+)
+
+// EscSeqParser parses ANSI escape sequences from text and tracks active formatting codes.
+// It supports both CSI (Control Sequence Introducer) and OSI (Operating System Command)
+// escape sequence formats.
+type EscSeqParser struct {
+ // codes tracks active escape sequence codes (e.g., 1 for bold, 3 for italic).
+ codes map[int]bool
+
+ // inEscSeq indicates whether the parser is currently inside an escape sequence.
+ inEscSeq bool
+ // escSeqKind identifies the type of escape sequence being parsed (CSI or OSI).
+ escSeqKind escSeqKind
+ // escapeSeq accumulates the current escape sequence being parsed.
+ escapeSeq string
+}
+
+func (s *EscSeqParser) Codes() []int {
+ codes := make([]int, 0)
+ for code, val := range s.codes {
+ if val {
+ codes = append(codes, code)
+ }
+ }
+ sort.Ints(codes)
+ return codes
+}
+
+func (s *EscSeqParser) Consume(char rune) {
+ if !s.inEscSeq && char == EscapeStartRune {
+ s.inEscSeq = true
+ s.escSeqKind = escSeqKindUnknown
+ s.escapeSeq = ""
+ } else if s.inEscSeq && s.escSeqKind == escSeqKindUnknown {
+ switch char {
+ case EscapeStartRuneCSI:
+ s.escSeqKind = escSeqKindCSI
+ case EscapeStartRuneOSI:
+ s.escSeqKind = escSeqKindOSI
+ }
}
- if seqKind == escSeqKindOSI {
- seq = strings.Replace(seq, EscapeStartOSI, "", 1)
- seq = strings.Replace(seq, EscapeStopOSI, "", 1)
- } else { // escSeqKindCSI
- seq = strings.Replace(seq, EscapeStartCSI, "", 1)
- seq = strings.Replace(seq, EscapeStopCSI, "", 1)
- }
+ if s.inEscSeq {
+ s.escapeSeq += string(char)
- codes := strings.Split(seq, ";")
- for _, code := range codes {
- code = strings.TrimSpace(code)
- if codeNum, err := strconv.Atoi(code); err == nil {
- switch codeNum {
- case escCodeResetAll:
- s.codes = make(map[int]bool) // clear everything
- case escCodeResetIntensity:
- delete(s.codes, escCodeBold)
- delete(s.codes, escCodeDim)
- case escCodeResetItalic:
- delete(s.codes, escCodeItalic)
- case escCodeResetUnderline:
- delete(s.codes, escCodeUnderline)
- case escCodeResetBlink:
- delete(s.codes, escCodeBlinkSlow)
- delete(s.codes, escCodeBlinkRapid)
- case escCodeResetReverse:
- delete(s.codes, escCodeReverse)
- case escCodeResetCrossedOut:
- delete(s.codes, escCodeCrossedOut)
- default:
- s.codes[codeNum] = true
- }
+ // --- FIX for OSC 8 hyperlinks (e.g. \x1b]8;;url\x07label\x1b]8;;\x07)
+ if s.escSeqKind == escSeqKindOSI &&
+ strings.HasPrefix(s.escapeSeq, escapeStartConcealOSI) &&
+ char == escRuneBEL { // BEL
+
+ s.ParseSeq(s.escapeSeq, s.escSeqKind)
+ s.Reset()
+ return
+ }
+
+ if s.isEscapeStopRune(char) {
+ s.ParseSeq(s.escapeSeq, s.escSeqKind)
+ s.Reset()
}
}
}
-func (s *escSeqParser) ParseString(str string) string {
+func (s *EscSeqParser) InSequence() bool {
+ return s.inEscSeq
+}
+
+func (s *EscSeqParser) IsOpen() bool {
+ return len(s.codes) > 0
+}
+
+func (s *EscSeqParser) ParseSeq(seq string, seqKind escSeqKind) {
+ if s.codes == nil {
+ s.codes = make(map[int]bool)
+ }
+
+ seq = s.stripEscapeSequence(seq, seqKind)
+ codes := s.splitAndTrimCodes(seq)
+ processed256ColorIndices := s.process256ColorSequences(codes)
+ s.processRegularCodes(codes, processed256ColorIndices)
+}
+
+func (s *EscSeqParser) ParseString(str string) string {
s.escapeSeq, s.inEscSeq, s.escSeqKind = "", false, escSeqKindUnknown
for _, char := range str {
s.Consume(char)
@@ -177,15 +193,33 @@ func (s *escSeqParser) ParseString(str string) string {
return s.Sequence()
}
-func (s *escSeqParser) Sequence() string {
+func (s *EscSeqParser) Reset() {
+ s.inEscSeq = false
+ s.escSeqKind = escSeqKindUnknown
+ s.escapeSeq = ""
+}
+
+func (s *EscSeqParser) Sequence() string {
out := strings.Builder{}
if s.IsOpen() {
out.WriteString(EscapeStart)
- for idx, code := range s.Codes() {
+ codes := s.Codes()
+ for idx, code := range codes {
if idx > 0 {
out.WriteRune(';')
}
- out.WriteString(fmt.Sprint(code))
+ // Check if this is a 256-color foreground code (1000-1255)
+ if code >= escCode256FgBase && code <= escCode256FgBase+escCode256Max {
+ colorIndex := code - escCode256FgBase
+ out.WriteString(fmt.Sprintf("%d;%d;%d", escCode256FgStart, escCode256Color, colorIndex))
+ } else if code >= escCode256BgBase && code <= escCode256BgBase+escCode256Max {
+ // 256-color background code (2000-2255)
+ colorIndex := code - escCode256BgBase
+ out.WriteString(fmt.Sprintf("%d;%d;%d", escCode256BgStart, escCode256Color, colorIndex))
+ } else {
+ // Regular code
+ out.WriteString(fmt.Sprint(code))
+ }
}
out.WriteString(EscapeStop)
}
@@ -193,12 +227,54 @@ func (s *escSeqParser) Sequence() string {
return out.String()
}
-const (
- escapeStartConcealOSI = "\x1b]8;"
- escapeStopConcealOSI = "\x1b\\"
-)
+// clearAllBackgroundColors clears all background color codes.
+func (s *EscSeqParser) clearAllBackgroundColors() {
+ for code := escCodeBgStdStart; code <= escCodeBgStdEnd; code++ {
+ delete(s.codes, code)
+ }
+ for code := escCodeBgBrightStart; code <= escCodeBgBrightEnd; code++ {
+ delete(s.codes, code)
+ }
+ for code := escCode256BgBase; code <= escCode256BgBase+escCode256Max; code++ {
+ delete(s.codes, code)
+ }
+}
-func (s *escSeqParser) isEscapeStopRune(char rune) bool {
+// clearAllForegroundColors clears all foreground color codes.
+func (s *EscSeqParser) clearAllForegroundColors() {
+ for code := escCodeFgStdStart; code <= escCodeFgStdEnd; code++ {
+ delete(s.codes, code)
+ }
+ for code := escCodeFgBrightStart; code <= escCodeFgBrightEnd; code++ {
+ delete(s.codes, code)
+ }
+ for code := escCode256FgBase; code <= escCode256FgBase+escCode256Max; code++ {
+ delete(s.codes, code)
+ }
+}
+
+// clearColorRange clears standard foreground or background colors.
+func (s *EscSeqParser) clearColorRange(isForeground bool) {
+ if isForeground {
+ // Clear standard foreground colors (30-37, 90-97)
+ for code := escCodeFgStdStart; code <= escCodeFgStdEnd; code++ {
+ delete(s.codes, code)
+ }
+ for code := escCodeFgBrightStart; code <= escCodeFgBrightEnd; code++ {
+ delete(s.codes, code)
+ }
+ } else {
+ // Clear standard background colors (40-47, 100-107)
+ for code := escCodeBgStdStart; code <= escCodeBgStdEnd; code++ {
+ delete(s.codes, code)
+ }
+ for code := escCodeBgBrightStart; code <= escCodeBgBrightEnd; code++ {
+ delete(s.codes, code)
+ }
+ }
+}
+
+func (s *EscSeqParser) isEscapeStopRune(char rune) bool {
if strings.HasPrefix(s.escapeSeq, escapeStartConcealOSI) {
if strings.HasSuffix(s.escapeSeq, escapeStopConcealOSI) {
return true
@@ -209,3 +285,140 @@ func (s *escSeqParser) isEscapeStopRune(char rune) bool {
}
return false
}
+
+// isRegularCode checks if a code is a regular code (not a 256-color encoded value).
+func (s *EscSeqParser) isRegularCode(codeNum int) bool {
+ return codeNum < escCode256FgBase || codeNum > escCode256BgBase+escCode256Max
+}
+
+// parse256ColorSequence attempts to parse a 256-color sequence starting at index i.
+// Returns (colorIndex, base, true) if valid, or (0, 0, false) if not.
+func (s *EscSeqParser) parse256ColorSequence(codes []string, i int) (colorIndex int, base int, ok bool) {
+ if i+2 >= len(codes) {
+ return 0, 0, false
+ }
+
+ codeNum, err := strconv.Atoi(codes[i])
+ if err != nil {
+ return 0, 0, false
+ }
+
+ var expectedBase int
+ switch codeNum {
+ case escCode256FgStart:
+ expectedBase = escCode256FgBase
+ case escCode256BgStart:
+ expectedBase = escCode256BgBase
+ default:
+ return 0, 0, false
+ }
+
+ nextCode, err := strconv.Atoi(codes[i+1])
+ if err != nil || nextCode != escCode256Color {
+ return 0, 0, false
+ }
+
+ colorIndex, err = strconv.Atoi(codes[i+2])
+ if err != nil || colorIndex < 0 || colorIndex > escCode256Max {
+ return 0, 0, false
+ }
+
+ return colorIndex, expectedBase, true
+}
+
+// process256ColorSequences processes 256-color sequences (38;5;n or 48;5;n) and returns
+// a map of indices that were part of valid 256-color sequences.
+func (s *EscSeqParser) process256ColorSequences(codes []string) map[int]bool {
+ processedIndices := make(map[int]bool)
+ for i := 0; i < len(codes); i++ {
+ if colorIndex, base, ok := s.parse256ColorSequence(codes, i); ok {
+ s.set256Color(base, colorIndex)
+ s.clearColorRange(base == escCode256FgBase)
+ processedIndices[i] = true
+ processedIndices[i+1] = true
+ processedIndices[i+2] = true
+ i += 2 // Skip i+1 and i+2 (loop will increment to i+3)
+ }
+ }
+ return processedIndices
+}
+
+// processCode handles a single escape code.
+func (s *EscSeqParser) processCode(codeNum int) {
+ switch codeNum {
+ case escCodeResetAll:
+ s.codes = make(map[int]bool)
+ case escCodeResetIntensity:
+ delete(s.codes, escCodeBold)
+ delete(s.codes, escCodeDim)
+ case escCodeResetItalic:
+ delete(s.codes, escCodeItalic)
+ case escCodeResetUnderline:
+ delete(s.codes, escCodeUnderline)
+ case escCodeResetBlink:
+ delete(s.codes, escCodeBlinkSlow)
+ delete(s.codes, escCodeBlinkRapid)
+ case escCodeResetReverse:
+ delete(s.codes, escCodeReverse)
+ case escCodeResetCrossedOut:
+ delete(s.codes, escCodeCrossedOut)
+ case escCodeResetFg:
+ s.clearAllForegroundColors()
+ case escCodeResetBg:
+ s.clearAllBackgroundColors()
+ default:
+ if s.isRegularCode(codeNum) {
+ s.codes[codeNum] = true
+ }
+ }
+}
+
+// processRegularCodes processes regular escape codes and reset codes.
+func (s *EscSeqParser) processRegularCodes(codes []string, processedIndices map[int]bool) {
+ for i, code := range codes {
+ if processedIndices[i] {
+ continue
+ }
+
+ codeNum, err := strconv.Atoi(code)
+ if err != nil {
+ continue
+ }
+
+ s.processCode(codeNum)
+ }
+}
+
+// set256Color sets a 256-color code and clears conflicting colors.
+func (s *EscSeqParser) set256Color(base int, colorIndex int) {
+ encodedValue := base + colorIndex
+ s.codes[encodedValue] = true
+
+ // Clear other colors in the same range
+ for code := base; code <= base+escCode256Max; code++ {
+ if code != encodedValue {
+ delete(s.codes, code)
+ }
+ }
+}
+
+// splitAndTrimCodes splits the sequence by semicolons and trims whitespace.
+func (s *EscSeqParser) splitAndTrimCodes(seq string) []string {
+ codes := strings.Split(seq, ";")
+ for i := range codes {
+ codes[i] = strings.TrimSpace(codes[i])
+ }
+ return codes
+}
+
+// stripEscapeSequence removes escape sequence markers from the input string.
+func (s *EscSeqParser) stripEscapeSequence(seq string, seqKind escSeqKind) string {
+ if seqKind == escSeqKindOSI {
+ seq = strings.Replace(seq, EscapeStartOSI, "", 1)
+ seq = strings.Replace(seq, EscapeStopOSI, "", 1)
+ } else {
+ seq = strings.Replace(seq, EscapeStartCSI, "", 1)
+ seq = strings.Replace(seq, EscapeStopCSI, "", 1)
+ }
+ return seq
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/string.go b/vendor/github.com/jedib0t/go-pretty/v6/text/string.go
index fa28a4c0..fa3e4f1d 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/text/string.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/string.go
@@ -28,7 +28,7 @@ func InsertEveryN(str string, runeToInsert rune, n int) string {
sLen := StringWidthWithoutEscSequences(str)
var out strings.Builder
out.Grow(sLen + (sLen / n))
- outLen, esp := 0, escSeqParser{}
+ outLen, esp := 0, EscSeqParser{}
for idx, c := range str {
if esp.InSequence() {
esp.Consume(c)
@@ -52,7 +52,7 @@ func InsertEveryN(str string, runeToInsert rune, n int) string {
//
// LongestLineLen("Ghost!\nCome back here!\nRight now!") == 15
func LongestLineLen(str string) int {
- maxLength, currLength, esp := 0, 0, escSeqParser{}
+ maxLength, currLength, esp := 0, 0, EscSeqParser{}
//fmt.Println(str)
for _, c := range str {
//fmt.Printf("%03d | %03d | %c | %5v | %v | %#v\n", idx, c, c, esp.inEscSeq, esp.Codes(), esp.escapeSeq)
@@ -76,17 +76,20 @@ func LongestLineLen(str string) int {
return maxLength
}
-// OverrideRuneWidthEastAsianWidth can *probably* help with alignment, and
-// length calculation issues when dealing with Unicode character-set and a
-// non-English language set in the LANG variable.
+// OverrideRuneWidthEastAsianWidth overrides the East Asian width detection in
+// the runewidth library. This is primarily for advanced use cases.
//
-// Set this to 'false' to force the "runewidth" library to pretend to deal with
-// English character-set. Be warned that if the text/content you are dealing
-// with contains East Asian character-set, this may result in unexpected
-// behavior.
+// Box drawing (U+2500-U+257F) and block element (U+2580-U+259F) characters
+// are automatically handled and always reported as width 1, regardless of
+// this setting, fixing alignment issues that previously required setting this
+// to false.
//
-// References:
-// * https://github.com/mattn/go-runewidth/issues/64#issuecomment-1221642154
+// Setting this to false forces runewidth to treat all characters as if in an
+// English locale. Warning: this may cause East Asian characters (Chinese,
+// Japanese, Korean) to be incorrectly reported as width 1 instead of 2.
+//
+// See:
+// * https://github.com/mattn/go-runewidth/issues/64
// * https://github.com/jedib0t/go-pretty/issues/220
// * https://github.com/jedib0t/go-pretty/issues/204
func OverrideRuneWidthEastAsianWidth(val bool) {
@@ -184,16 +187,28 @@ func RuneCount(str string) int {
return StringWidthWithoutEscSequences(str)
}
-// RuneWidth returns the mostly accurate character-width of the rune. This is
-// not 100% accurate as the character width is usually dependent on the
-// typeface (font) used in the console/terminal. For ex.:
+// RuneWidth returns the display width of a rune. Width accuracy depends on
+// the terminal font, as character width is font-dependent. Examples:
//
// RuneWidth('A') == 1
// RuneWidth('ツ') == 2
// RuneWidth('⊙') == 1
// RuneWidth('︿') == 2
// RuneWidth(0x27) == 0
+//
+// Box drawing (U+2500-U+257F) and block element (U+2580-U+259F) characters
+// are always treated as width 1, regardless of locale, to ensure proper
+// alignment in tables and progress indicators. This fixes incorrect width 2
+// reporting in East Asian locales (e.g., LANG=zh_CN.UTF-8).
+//
+// See:
+// * https://github.com/mattn/go-runewidth/issues/64
+// * https://github.com/jedib0t/go-pretty/issues/220
+// * https://github.com/jedib0t/go-pretty/issues/204
func RuneWidth(r rune) int {
+ if (r >= 0x2500 && r <= 0x257F) || (r >= 0x2580 && r <= 0x259F) {
+ return 1
+ }
return rwCondition.RuneWidth(r)
}
@@ -248,7 +263,7 @@ func StringWidth(str string) int {
// StringWidthWithoutEscSequences("Ghost 生命"): 10
// StringWidthWithoutEscSequences("\x1b[33mGhost 生命\x1b[0m"): 10
func StringWidthWithoutEscSequences(str string) int {
- count, esp := 0, escSeqParser{}
+ count, esp := 0, EscSeqParser{}
for _, c := range str {
if esp.InSequence() {
esp.Consume(c)
@@ -277,7 +292,7 @@ func Trim(str string, maxLen int) string {
var out strings.Builder
out.Grow(maxLen)
- outLen, esp := 0, escSeqParser{}
+ outLen, esp := 0, EscSeqParser{}
for _, sChr := range str {
if esp.InSequence() {
esp.Consume(sChr)
@@ -306,7 +321,7 @@ func Widen(str string) string {
sb := strings.Builder{}
sb.Grow(len(str))
- esp := escSeqParser{}
+ esp := EscSeqParser{}
for _, c := range str {
if esp.InSequence() {
sb.WriteRune(c)
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/transformer.go b/vendor/github.com/jedib0t/go-pretty/v6/text/transformer.go
index 193a721c..ef122272 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/text/transformer.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/transformer.go
@@ -11,9 +11,15 @@ import (
// Transformer related constants
const (
- unixTimeMinMilliseconds = int64(10000000000)
- unixTimeMinMicroseconds = unixTimeMinMilliseconds * 1000
- unixTimeMinNanoSeconds = unixTimeMinMicroseconds * 1000
+ // Pre-computed time conversion constants to avoid repeated calculations
+ nanosPerSecond = int64(time.Second)
+ microsPerSecond = nanosPerSecond / 1000
+ millisPerSecond = nanosPerSecond / 1000000
+
+ // Thresholds for detecting unix timestamp units (10 seconds worth in each unit)
+ unixTimeMinMilliseconds = 10 * nanosPerSecond
+ unixTimeMinMicroseconds = 10 * nanosPerSecond * 1000
+ unixTimeMinNanoSeconds = 10 * nanosPerSecond * 1000000
)
// Transformer related variables
@@ -40,103 +46,84 @@ type Transformer func(val interface{}) string
// - transforms the number as directed by 'format' (ex.: %.2f)
// - colors negative values Red
// - colors positive values Green
+//
+//gocyclo:ignore
func NewNumberTransformer(format string) Transformer {
+ // Pre-compute negative format string to avoid repeated allocations
+ negFormat := "-" + format
+
+ transformInt64 := func(val int64) string {
+ if val < 0 {
+ return colorsNumberNegative.Sprintf(negFormat, -val)
+ }
+ if val > 0 {
+ return colorsNumberPositive.Sprintf(format, val)
+ }
+ return colorsNumberZero.Sprintf(format, val)
+ }
+
+ transformUint64 := func(val uint64) string {
+ if val > 0 {
+ return colorsNumberPositive.Sprintf(format, val)
+ }
+ return colorsNumberZero.Sprintf(format, val)
+ }
+
+ transformFloat64 := func(val float64) string {
+ if val < 0 {
+ return colorsNumberNegative.Sprintf(negFormat, -val)
+ }
+ if val > 0 {
+ return colorsNumberPositive.Sprintf(format, val)
+ }
+ return colorsNumberZero.Sprintf(format, val)
+ }
+
+ // Use type switch for O(1) type checking instead of sequential type assertions
return func(val interface{}) string {
- if valStr := transformInt(format, val); valStr != "" {
- return valStr
+ switch v := val.(type) {
+ case int:
+ return transformInt64(int64(v))
+ case int8:
+ return transformInt64(int64(v))
+ case int16:
+ return transformInt64(int64(v))
+ case int32:
+ return transformInt64(int64(v))
+ case int64:
+ return transformInt64(v)
+ case uint:
+ return transformUint64(uint64(v))
+ case uint8:
+ return transformUint64(uint64(v))
+ case uint16:
+ return transformUint64(uint64(v))
+ case uint32:
+ return transformUint64(uint64(v))
+ case uint64:
+ return transformUint64(v)
+ case float32:
+ return transformFloat64(float64(v))
+ case float64:
+ return transformFloat64(v)
+ default:
+ return fmt.Sprint(val)
}
- if valStr := transformUint(format, val); valStr != "" {
- return valStr
- }
- if valStr := transformFloat(format, val); valStr != "" {
- return valStr
- }
- return fmt.Sprint(val)
}
}
-func transformInt(format string, val interface{}) string {
- transform := func(val int64) string {
- if val < 0 {
- return colorsNumberNegative.Sprintf("-"+format, -val)
- }
- if val > 0 {
- return colorsNumberPositive.Sprintf(format, val)
- }
- return colorsNumberZero.Sprintf(format, val)
- }
-
- if number, ok := val.(int); ok {
- return transform(int64(number))
- }
- if number, ok := val.(int8); ok {
- return transform(int64(number))
- }
- if number, ok := val.(int16); ok {
- return transform(int64(number))
- }
- if number, ok := val.(int32); ok {
- return transform(int64(number))
- }
- if number, ok := val.(int64); ok {
- return transform(number)
- }
- return ""
-}
-
-func transformUint(format string, val interface{}) string {
- transform := func(val uint64) string {
- if val > 0 {
- return colorsNumberPositive.Sprintf(format, val)
- }
- return colorsNumberZero.Sprintf(format, val)
- }
-
- if number, ok := val.(uint); ok {
- return transform(uint64(number))
- }
- if number, ok := val.(uint8); ok {
- return transform(uint64(number))
- }
- if number, ok := val.(uint16); ok {
- return transform(uint64(number))
- }
- if number, ok := val.(uint32); ok {
- return transform(uint64(number))
- }
- if number, ok := val.(uint64); ok {
- return transform(number)
- }
- return ""
-}
-
-func transformFloat(format string, val interface{}) string {
- transform := func(val float64) string {
- if val < 0 {
- return colorsNumberNegative.Sprintf("-"+format, -val)
- }
- if val > 0 {
- return colorsNumberPositive.Sprintf(format, val)
- }
- return colorsNumberZero.Sprintf(format, val)
- }
-
- if number, ok := val.(float32); ok {
- return transform(float64(number))
- }
- if number, ok := val.(float64); ok {
- return transform(number)
- }
- return ""
-}
-
// NewJSONTransformer returns a Transformer that can format a JSON string or an
// object into pretty-indented JSON-strings.
func NewJSONTransformer(prefix string, indent string) Transformer {
return func(val interface{}) string {
if valStr, ok := val.(string); ok {
+ valStr = strings.TrimSpace(valStr)
+ // Validate JSON before attempting to indent to avoid unnecessary processing
+ if !json.Valid([]byte(valStr)) {
+ return fmt.Sprintf("%#v", valStr)
+ }
var b bytes.Buffer
- if err := json.Indent(&b, []byte(strings.TrimSpace(valStr)), prefix, indent); err == nil {
+ if err := json.Indent(&b, []byte(valStr), prefix, indent); err == nil {
return b.String()
}
} else if b, err := json.MarshalIndent(val, prefix, indent); err == nil {
@@ -154,17 +141,17 @@ func NewJSONTransformer(prefix string, indent string) Transformer {
// location (use time.Local to get localized timestamps).
func NewTimeTransformer(layout string, location *time.Location) Transformer {
return func(val interface{}) string {
- rsp := fmt.Sprint(val)
+ // Check for time.Time first to avoid unnecessary fmt.Sprint conversion
if valTime, ok := val.(time.Time); ok {
- rsp = formatTime(valTime, layout, location)
- } else {
- // cycle through some supported layouts to see if the string form
- // of the object matches any of these layouts
- for _, possibleTimeLayout := range possibleTimeLayouts {
- if valTime, err := time.Parse(possibleTimeLayout, rsp); err == nil {
- rsp = formatTime(valTime, layout, location)
- break
- }
+ return formatTime(valTime, layout, location)
+ }
+ // Only convert to string if not already time.Time
+ rsp := fmt.Sprint(val)
+ // Cycle through some supported layouts to see if the string form
+ // of the object matches any of these layouts
+ for _, possibleTimeLayout := range possibleTimeLayouts {
+ if valTime, err := time.Parse(possibleTimeLayout, rsp); err == nil {
+ return formatTime(valTime, layout, location)
}
}
return rsp
@@ -217,12 +204,13 @@ func formatTime(t time.Time, layout string, location *time.Location) string {
}
func formatTimeUnix(unixTime int64, timeTransformer Transformer) string {
+ // Use pre-computed constants instead of repeated time.Second.Nanoseconds() calls
if unixTime >= unixTimeMinNanoSeconds {
- unixTime = unixTime / time.Second.Nanoseconds()
+ unixTime = unixTime / nanosPerSecond
} else if unixTime >= unixTimeMinMicroseconds {
- unixTime = unixTime / (time.Second.Nanoseconds() / 1000)
+ unixTime = unixTime / microsPerSecond
} else if unixTime >= unixTimeMinMilliseconds {
- unixTime = unixTime / (time.Second.Nanoseconds() / 1000000)
+ unixTime = unixTime / millisPerSecond
}
return timeTransformer(time.Unix(unixTime, 0))
}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/wrap.go b/vendor/github.com/jedib0t/go-pretty/v6/text/wrap.go
index 8ad84855..8a9e803d 100644
--- a/vendor/github.com/jedib0t/go-pretty/v6/text/wrap.go
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/wrap.go
@@ -157,7 +157,7 @@ func terminateOutput(lastSeenEscSeq string, out *strings.Builder) {
}
func wrapHard(paragraph string, wrapLen int, out *strings.Builder) {
- esp := escSeqParser{}
+ esp := EscSeqParser{}
lineLen, lastSeenEscSeq := 0, ""
words := strings.Fields(paragraph)
for wordIdx, word := range words {
@@ -186,7 +186,7 @@ func wrapHard(paragraph string, wrapLen int, out *strings.Builder) {
}
func wrapSoft(paragraph string, wrapLen int, out *strings.Builder) {
- esp := escSeqParser{}
+ esp := EscSeqParser{}
lineLen, lastSeenEscSeq := 0, ""
words := strings.Fields(paragraph)
for wordIdx, word := range words {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index b901b603..0800d91d 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -254,7 +254,7 @@ github.com/imdario/mergo
# github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0
## explicit
github.com/jaytaylor/html2text
-# github.com/jedib0t/go-pretty/v6 v6.6.8
+# github.com/jedib0t/go-pretty/v6 v6.7.7
## explicit; go 1.18
github.com/jedib0t/go-pretty/v6/table
github.com/jedib0t/go-pretty/v6/text