mirror of
https://github.com/crazy-max/diun.git
synced 2026-01-02 19:17:39 +01:00
chore(deps): bump github.com/jedib0t/go-pretty/v6 from 6.6.8 to 6.7.7
Bumps [github.com/jedib0t/go-pretty/v6](https://github.com/jedib0t/go-pretty) from 6.6.8 to 6.7.7. - [Release notes](https://github.com/jedib0t/go-pretty/releases) - [Commits](https://github.com/jedib0t/go-pretty/compare/v6.6.8...v6.7.7) --- updated-dependencies: - dependency-name: github.com/jedib0t/go-pretty/v6 dependency-version: 6.7.7 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
2
go.mod
2
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
|
||||
|
||||
4
go.sum
4
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=
|
||||
|
||||
650
vendor/github.com/jedib0t/go-pretty/v6/table/EXAMPLES.md
generated
vendored
Normal file
650
vendor/github.com/jedib0t/go-pretty/v6/table/EXAMPLES.md
generated
vendored
Normal file
@@ -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 | |
|
||||
+-----+------------+-----------+--------+-----------------------------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>Ready-to-use Styles</strong></summary>
|
||||
|
||||
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:
|
||||
|
||||
<img src="images/table-StyleColoredBright.png" width="640px" alt="Colored Table"/>
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>Roll your own Style</strong></summary>
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>Customize Horizontal Separators</strong></summary>
|
||||
|
||||
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.
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>Auto-Merge</strong></summary>
|
||||
|
||||
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 │
|
||||
└───┴─────────┴────────┴───────────┴───────────┴─────┴─────┘
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>Paging</strong></summary>
|
||||
|
||||
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 | |
|
||||
+-----+------------+-----------+--------+-----------------------------+
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>Filtering</strong></summary>
|
||||
|
||||
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 |
|
||||
+-----+------------+-----------+--------+
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>Sorting</strong></summary>
|
||||
|
||||
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},
|
||||
})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>Sorting Customization</strong></summary>
|
||||
|
||||
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
|
||||
})
|
||||
```
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>Wrapping (or) Row/Column Width restrictions</strong></summary>
|
||||
|
||||
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 | ~
|
||||
+-----+------------+-----------+--------+------- ~
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>Column Control - Alignment, Colors, Width and more</strong></summary>
|
||||
|
||||
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,
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>CSV</strong></summary>
|
||||
|
||||
```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,
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>HTML Table</strong></summary>
|
||||
|
||||
```golang
|
||||
t.Style().HTML = table.HTMLOptions{
|
||||
CSSClass: "game-of-thrones",
|
||||
EmptyColumn: " ",
|
||||
EscapeText: true,
|
||||
Newline: "<br/>",
|
||||
ConvertColorsToSpans: true, // Convert ANSI escape sequences to HTML <span> tags with CSS classes
|
||||
}
|
||||
t.RenderHTML()
|
||||
```
|
||||
to get:
|
||||
```html
|
||||
<table class="game-of-thrones">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="right">#</th>
|
||||
<th>First Name</th>
|
||||
<th>Last Name</th>
|
||||
<th align="right">Salary</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="right">1</td>
|
||||
<td>Arya</td>
|
||||
<td>Stark</td>
|
||||
<td align="right">3000</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">20</td>
|
||||
<td>Jon</td>
|
||||
<td>Snow</td>
|
||||
<td align="right">2000</td>
|
||||
<td>You know nothing, Jon Snow!</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">300</td>
|
||||
<td>Tyrion</td>
|
||||
<td>Lannister</td>
|
||||
<td align="right">5000</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td align="right"> </td>
|
||||
<td> </td>
|
||||
<td>Total</td>
|
||||
<td align="right">10000</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>Markdown Table</strong></summary>
|
||||
|
||||
```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 | |
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
513
vendor/github.com/jedib0t/go-pretty/v6/table/README.md
generated
vendored
513
vendor/github.com/jedib0t/go-pretty/v6/table/README.md
generated
vendored
@@ -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
|
||||
|
||||
<img src="images/table-StyleColoredBright.png" width="640px" alt="Colored Table"/>
|
||||
### 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: "<br/>",
|
||||
}
|
||||
t.RenderHTML()
|
||||
```
|
||||
to get:
|
||||
```html
|
||||
<table class="game-of-thrones">
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="right">#</th>
|
||||
<th>First Name</th>
|
||||
<th>Last Name</th>
|
||||
<th align="right">Salary</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="right">1</td>
|
||||
<td>Arya</td>
|
||||
<td>Stark</td>
|
||||
<td align="right">3000</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">20</td>
|
||||
<td>Jon</td>
|
||||
<td>Snow</td>
|
||||
<td align="right">2000</td>
|
||||
<td>You know nothing, Jon Snow!</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">300</td>
|
||||
<td>Tyrion</td>
|
||||
<td>Lannister</td>
|
||||
<td align="right">5000</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td align="right"> </td>
|
||||
<td> </td>
|
||||
<td>Total</td>
|
||||
<td align="right">10000</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
```
|
||||
|
||||
### ... 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`)
|
||||
|
||||
250
vendor/github.com/jedib0t/go-pretty/v6/table/filter.go
generated
vendored
Normal file
250
vendor/github.com/jedib0t/go-pretty/v6/table/filter.go
generated
vendored
Normal file
@@ -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)
|
||||
}
|
||||
87
vendor/github.com/jedib0t/go-pretty/v6/table/render.go
generated
vendored
87
vendor/github.com/jedib0t/go-pretty/v6/table/render.go
generated
vendored
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/jedib0t/go-pretty/v6/table/render_csv.go
generated
vendored
2
vendor/github.com/jedib0t/go-pretty/v6/table/render_csv.go
generated
vendored
@@ -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 {
|
||||
|
||||
11
vendor/github.com/jedib0t/go-pretty/v6/table/render_hint.go
generated
vendored
11
vendor/github.com/jedib0t/go-pretty/v6/table/render_hint.go
generated
vendored
@@ -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"
|
||||
)
|
||||
|
||||
10
vendor/github.com/jedib0t/go-pretty/v6/table/render_html.go
generated
vendored
10
vendor/github.com/jedib0t/go-pretty/v6/table/render_html.go
generated
vendored
@@ -60,7 +60,7 @@ const (
|
||||
// </tfoot>
|
||||
// </table>
|
||||
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)
|
||||
}
|
||||
|
||||
184
vendor/github.com/jedib0t/go-pretty/v6/table/render_init.go
generated
vendored
184
vendor/github.com/jedib0t/go-pretty/v6/table/render_init.go
generated
vendored
@@ -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
|
||||
}
|
||||
|
||||
39
vendor/github.com/jedib0t/go-pretty/v6/table/render_markdown.go
generated
vendored
39
vendor/github.com/jedib0t/go-pretty/v6/table/render_markdown.go
generated
vendored
@@ -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", "<br/>")
|
||||
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", "<br/>")
|
||||
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("# ")
|
||||
|
||||
2
vendor/github.com/jedib0t/go-pretty/v6/table/render_tsv.go
generated
vendored
2
vendor/github.com/jedib0t/go-pretty/v6/table/render_tsv.go
generated
vendored
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
func (t *Table) RenderTSV() string {
|
||||
t.initForRender()
|
||||
t.initForRender(renderModeTSV)
|
||||
|
||||
var out strings.Builder
|
||||
|
||||
|
||||
128
vendor/github.com/jedib0t/go-pretty/v6/table/sort.go
generated
vendored
128
vendor/github.com/jedib0t/go-pretty/v6/table/sort.go
generated
vendored
@@ -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)
|
||||
|
||||
592
vendor/github.com/jedib0t/go-pretty/v6/table/style.go
generated
vendored
592
vendor/github.com/jedib0t/go-pretty/v6/table/style.go
generated
vendored
@@ -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:
|
||||
// (-----^------------^-----------^--------^-----------------------------)
|
||||
// [< #>|<FIRST NAME>|<LAST NAME>|<SALARY>|< >]
|
||||
// {-----+------------+-----------+--------+-----------------------------}
|
||||
// [< 1>|<Arya >|<Stark >|< 3000>|< >]
|
||||
// [< 20>|<Jon >|<Snow >|< 2000>|<You know nothing, Jon Snow!>]
|
||||
// [<300>|<Tyrion >|<Lannister>|< 5000>|< >]
|
||||
// {-----+------------+-----------+--------+-----------------------------}
|
||||
// [< >|< >|<TOTAL >|< 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 <table> 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: "<br/>",
|
||||
}
|
||||
|
||||
// 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),
|
||||
}
|
||||
)
|
||||
|
||||
303
vendor/github.com/jedib0t/go-pretty/v6/table/style_box.go
generated
vendored
Normal file
303
vendor/github.com/jedib0t/go-pretty/v6/table/style_box.go
generated
vendored
Normal file
@@ -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:
|
||||
// (-----^------------^-----------^--------^-----------------------------)
|
||||
// [< #>|<FIRST NAME>|<LAST NAME>|<SALARY>|< >]
|
||||
// {-----+------------+-----------+--------+-----------------------------}
|
||||
// [< 1>|<Arya >|<Stark >|< 3000>|< >]
|
||||
// [< 20>|<Jon >|<Snow >|< 2000>|<You know nothing, Jon Snow!>]
|
||||
// [<300>|<Tyrion >|<Lannister>|< 5000>|< >]
|
||||
// {-----+------------+-----------+--------+-----------------------------}
|
||||
// [< >|< >|<TOTAL >|< 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
|
||||
}
|
||||
}
|
||||
139
vendor/github.com/jedib0t/go-pretty/v6/table/style_color.go
generated
vendored
Normal file
139
vendor/github.com/jedib0t/go-pretty/v6/table/style_color.go
generated
vendored
Normal file
@@ -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},
|
||||
}
|
||||
)
|
||||
32
vendor/github.com/jedib0t/go-pretty/v6/table/style_format.go
generated
vendored
Normal file
32
vendor/github.com/jedib0t/go-pretty/v6/table/style_format.go
generated
vendored
Normal file
@@ -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,
|
||||
}
|
||||
)
|
||||
21
vendor/github.com/jedib0t/go-pretty/v6/table/style_html.go
generated
vendored
Normal file
21
vendor/github.com/jedib0t/go-pretty/v6/table/style_html.go
generated
vendored
Normal file
@@ -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 <span> tags with CSS classes? EscapeText will be true if this is true.
|
||||
CSSClass string // CSS class to set on the overall <table> 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: "<br/>",
|
||||
}
|
||||
)
|
||||
109
vendor/github.com/jedib0t/go-pretty/v6/table/style_options.go
generated
vendored
Normal file
109
vendor/github.com/jedib0t/go-pretty/v6/table/style_options.go
generated
vendored
Normal file
@@ -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,
|
||||
}
|
||||
)
|
||||
21
vendor/github.com/jedib0t/go-pretty/v6/table/style_size.go
generated
vendored
Normal file
21
vendor/github.com/jedib0t/go-pretty/v6/table/style_size.go
generated
vendored
Normal file
@@ -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,
|
||||
}
|
||||
)
|
||||
81
vendor/github.com/jedib0t/go-pretty/v6/table/styles_title.go
generated
vendored
Normal file
81
vendor/github.com/jedib0t/go-pretty/v6/table/styles_title.go
generated
vendored
Normal file
@@ -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),
|
||||
}
|
||||
)
|
||||
78
vendor/github.com/jedib0t/go-pretty/v6/table/table.go
generated
vendored
78
vendor/github.com/jedib0t/go-pretty/v6/table/table.go
generated
vendored
@@ -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 <table> 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 {
|
||||
|
||||
44
vendor/github.com/jedib0t/go-pretty/v6/table/util.go
generated
vendored
44
vendor/github.com/jedib0t/go-pretty/v6/table/util.go
generated
vendored
@@ -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 {
|
||||
|
||||
151
vendor/github.com/jedib0t/go-pretty/v6/table/util_html.go
generated
vendored
Normal file
151
vendor/github.com/jedib0t/go-pretty/v6/table/util_html.go
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/text"
|
||||
)
|
||||
|
||||
// convertEscSequencesToSpans converts ANSI escape sequences to HTML <span> tags with CSS classes.
|
||||
func convertEscSequencesToSpans(str string) string {
|
||||
converter := newEscSeqToSpanConverter()
|
||||
return converter.Convert(str)
|
||||
}
|
||||
|
||||
// escSeqToSpanConverter converts ANSI escape sequences to HTML <span> 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 <span> 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("</span>")
|
||||
}
|
||||
|
||||
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("</span>")
|
||||
}
|
||||
}
|
||||
|
||||
// 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("<span class=\"")
|
||||
c.result.WriteString(class)
|
||||
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)
|
||||
}
|
||||
}
|
||||
1
vendor/github.com/jedib0t/go-pretty/v6/table/writer.go
generated
vendored
1
vendor/github.com/jedib0t/go-pretty/v6/table/writer.go
generated
vendored
@@ -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
|
||||
|
||||
143
vendor/github.com/jedib0t/go-pretty/v6/text/README.md
generated
vendored
143
vendor/github.com/jedib0t/go-pretty/v6/text/README.md
generated
vendored
@@ -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)).
|
||||
[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
|
||||
8
vendor/github.com/jedib0t/go-pretty/v6/text/ansi_unix.go
generated
vendored
8
vendor/github.com/jedib0t/go-pretty/v6/text/ansi_unix.go
generated
vendored
@@ -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"
|
||||
}
|
||||
|
||||
206
vendor/github.com/jedib0t/go-pretty/v6/text/color.go
generated
vendored
206
vendor/github.com/jedib0t/go-pretty/v6/text/color.go
generated
vendored
@@ -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
|
||||
}
|
||||
|
||||
433
vendor/github.com/jedib0t/go-pretty/v6/text/escape_seq_parser.go
generated
vendored
433
vendor/github.com/jedib0t/go-pretty/v6/text/escape_seq_parser.go
generated
vendored
@@ -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
|
||||
}
|
||||
|
||||
49
vendor/github.com/jedib0t/go-pretty/v6/text/string.go
generated
vendored
49
vendor/github.com/jedib0t/go-pretty/v6/text/string.go
generated
vendored
@@ -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)
|
||||
|
||||
190
vendor/github.com/jedib0t/go-pretty/v6/text/transformer.go
generated
vendored
190
vendor/github.com/jedib0t/go-pretty/v6/text/transformer.go
generated
vendored
@@ -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))
|
||||
}
|
||||
|
||||
4
vendor/github.com/jedib0t/go-pretty/v6/text/wrap.go
generated
vendored
4
vendor/github.com/jedib0t/go-pretty/v6/text/wrap.go
generated
vendored
@@ -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 {
|
||||
|
||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user