fix: improve display for strings and numbers in v1 shell tables (#420)
* fix: improve alignment for strings and numbers * add scientific toggle command for table format * enter altscreen when in table format interactive mode * chore: run go mod tidy
This commit is contained in:
@ -118,8 +118,9 @@ func newSugNodeFn(subsugs ...string) subsuggestFnType {
|
|||||||
func (c *Client) completer(d prompt.Document) []prompt.Suggest {
|
func (c *Client) completer(d prompt.Document) []prompt.Suggest {
|
||||||
// the commented-out lines are unsupported in 2.x
|
// the commented-out lines are unsupported in 2.x
|
||||||
suggestions := map[string]SuggestNode{
|
suggestions := map[string]SuggestNode{
|
||||||
"use": {Description: "Set current database", subsuggestFn: c.suggestUse},
|
"use": {Description: "Set current database", subsuggestFn: c.suggestUse},
|
||||||
"pretty": {Description: "Toggle pretty print for the json format"},
|
"pretty": {Description: "Toggle pretty print for the json format"},
|
||||||
|
"scientific": {Description: "Toggle scientific number format for the table format"},
|
||||||
"precision": {Description: "Specify the format of the timestamp",
|
"precision": {Description: "Specify the format of the timestamp",
|
||||||
subsuggestFn: newSugNodeFn(
|
subsuggestFn: newSugNodeFn(
|
||||||
"rfc3339",
|
"rfc3339",
|
||||||
|
@ -50,40 +50,58 @@ func NewModel(
|
|||||||
curRes int,
|
curRes int,
|
||||||
resMax int,
|
resMax int,
|
||||||
curSer int,
|
curSer int,
|
||||||
serMax int) Model {
|
serMax int,
|
||||||
|
scientific bool) Model {
|
||||||
|
|
||||||
cols := make([]table.Column, len(*res.Columns)+1)
|
cols := make([]table.Column, len(*res.Columns)+1)
|
||||||
colWidths := make([]int, len(*res.Columns)+1)
|
colWidths := make([]int, len(*res.Columns)+1)
|
||||||
|
alignment := make([]lipgloss.Position, len(*res.Columns)+1)
|
||||||
rows := make([]table.Row, len(*res.Values))
|
rows := make([]table.Row, len(*res.Values))
|
||||||
colNames := *res.Columns
|
colNames := *res.Columns
|
||||||
for rowI, row := range *res.Values {
|
for rowI, row := range *res.Values {
|
||||||
rd := table.RowData{}
|
rd := table.RowData{}
|
||||||
rd["index"] = fmt.Sprintf("%d", rowI+1)
|
rd["index"] = fmt.Sprintf("%d", rowI+1)
|
||||||
|
alignment[0] = lipgloss.Right
|
||||||
colWidths[0] = len("index") + colPadding
|
colWidths[0] = len("index") + colPadding
|
||||||
for colI, rowVal := range row {
|
for colI, rowVal := range row {
|
||||||
var item string
|
var item string
|
||||||
|
var colLen int
|
||||||
switch val := rowVal.(type) {
|
switch val := rowVal.(type) {
|
||||||
case int:
|
case int:
|
||||||
item = fmt.Sprintf("%d", val)
|
item = fmt.Sprintf("%d", val)
|
||||||
|
colLen = len(item)
|
||||||
|
alignment[colI+1] = lipgloss.Right
|
||||||
case string:
|
case string:
|
||||||
item = fmt.Sprintf("%q", val)
|
item = color.YellowString(val)
|
||||||
|
colLen = len(val)
|
||||||
|
alignment[colI+1] = lipgloss.Left
|
||||||
|
case float32, float64:
|
||||||
|
if scientific {
|
||||||
|
item = fmt.Sprintf("%.10e", val)
|
||||||
|
} else {
|
||||||
|
item = fmt.Sprintf("%.10f", val)
|
||||||
|
}
|
||||||
|
colLen = len(item)
|
||||||
|
alignment[colI+1] = lipgloss.Right
|
||||||
default:
|
default:
|
||||||
item = fmt.Sprintf("%v", val)
|
item = fmt.Sprintf("%v", val)
|
||||||
|
colLen = len(item)
|
||||||
|
alignment[colI+1] = lipgloss.Right
|
||||||
}
|
}
|
||||||
rd[colNames[colI]] = item
|
rd[colNames[colI]] = item
|
||||||
if colWidths[colI+1] < len(item)+colPadding {
|
if colWidths[colI+1] < colLen+colPadding {
|
||||||
colWidths[colI+1] = len(item) + colPadding
|
colWidths[colI+1] = colLen + colPadding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rows[rowI] = table.NewRow(rd).
|
rows[rowI] = table.NewRow(rd).
|
||||||
WithStyle(lipgloss.NewStyle().Align(lipgloss.Center))
|
WithStyle(lipgloss.NewStyle())
|
||||||
}
|
}
|
||||||
cols[0] = table.NewColumn("index", "index", colWidths[0])
|
cols[0] = table.NewColumn("index", "index", colWidths[0])
|
||||||
indexStyle := lipgloss.NewStyle()
|
indexStyle := lipgloss.NewStyle()
|
||||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||||
indexStyle = indexStyle.
|
indexStyle = indexStyle.
|
||||||
Faint(true).
|
Faint(true).
|
||||||
Align(lipgloss.Center)
|
Align(lipgloss.Right)
|
||||||
}
|
}
|
||||||
cols[0] = cols[0].WithStyle(indexStyle)
|
cols[0] = cols[0].WithStyle(indexStyle)
|
||||||
for colI, colTitle := range colNames {
|
for colI, colTitle := range colNames {
|
||||||
@ -91,7 +109,7 @@ func NewModel(
|
|||||||
colWidths[colI+1] = len(colTitle) + colPadding
|
colWidths[colI+1] = len(colTitle) + colPadding
|
||||||
}
|
}
|
||||||
cols[colI+1] = table.NewColumn(colTitle, color.HiCyanString(colTitle), colWidths[colI+1]).
|
cols[colI+1] = table.NewColumn(colTitle, color.HiCyanString(colTitle), colWidths[colI+1]).
|
||||||
WithStyle(lipgloss.NewStyle().Align(lipgloss.Center))
|
WithStyle(lipgloss.NewStyle().Align(alignment[colI+1]))
|
||||||
}
|
}
|
||||||
colNames = append([]string{"index"}, colNames...)
|
colNames = append([]string{"index"}, colNames...)
|
||||||
screenPadding := 10
|
screenPadding := 10
|
||||||
@ -122,6 +140,7 @@ func NewModel(
|
|||||||
keybind.FilterClear.Unbind()
|
keybind.FilterClear.Unbind()
|
||||||
|
|
||||||
m.simpleTable = table.New(m.allCols).
|
m.simpleTable = table.New(m.allCols).
|
||||||
|
HeaderStyle(lipgloss.NewStyle().Align(lipgloss.Center)).
|
||||||
WithRows(m.rows).
|
WithRows(m.rows).
|
||||||
WithPageSize(15).
|
WithPageSize(15).
|
||||||
WithMaxTotalWidth(500).
|
WithMaxTotalWidth(500).
|
||||||
@ -143,7 +162,11 @@ func NewModel(
|
|||||||
func (m Model) Init() tea.Cmd {
|
func (m Model) Init() tea.Cmd {
|
||||||
color.Magenta("Interactive Table View (press q to exit mode, shift+up/down to navigate tables):")
|
color.Magenta("Interactive Table View (press q to exit mode, shift+up/down to navigate tables):")
|
||||||
builder := strings.Builder{}
|
builder := strings.Builder{}
|
||||||
fmt.Printf("Name: %s\n", color.GreenString(m.name))
|
if m.name != "" {
|
||||||
|
fmt.Printf("Name: %s\n", color.GreenString(m.name))
|
||||||
|
} else {
|
||||||
|
fmt.Println("") // keep a consistent height, so print an empty line
|
||||||
|
}
|
||||||
if len(m.tags) > 0 {
|
if len(m.tags) > 0 {
|
||||||
fmt.Print("Tags: ")
|
fmt.Print("Tags: ")
|
||||||
for key, val := range m.tags {
|
for key, val := range m.tags {
|
||||||
@ -204,9 +227,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
} else {
|
} else {
|
||||||
m.simpleTable = m.simpleTable.PageDown()
|
m.simpleTable = m.simpleTable.PageDown()
|
||||||
}
|
}
|
||||||
case "<":
|
case "[":
|
||||||
m.simpleTable = m.simpleTable.PageFirst()
|
m.simpleTable = m.simpleTable.PageFirst()
|
||||||
case ">":
|
case "]":
|
||||||
m.simpleTable = m.simpleTable.PageLast()
|
m.simpleTable = m.simpleTable.PageLast()
|
||||||
}
|
}
|
||||||
case tea.WindowSizeMsg:
|
case tea.WindowSizeMsg:
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/influxdata/go-prompt"
|
"github.com/influxdata/go-prompt"
|
||||||
"github.com/influxdata/influx-cli/v2/api"
|
"github.com/influxdata/influx-cli/v2/api"
|
||||||
"github.com/influxdata/influx-cli/v2/clients"
|
"github.com/influxdata/influx-cli/v2/clients"
|
||||||
|
"github.com/muesli/termenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
@ -42,6 +43,7 @@ type PersistentQueryParams struct {
|
|||||||
Precision string
|
Precision string
|
||||||
Format FormatType
|
Format FormatType
|
||||||
Pretty bool
|
Pretty bool
|
||||||
|
Scientific bool
|
||||||
// Autocompletion Storage
|
// Autocompletion Storage
|
||||||
historyFilePath string
|
historyFilePath string
|
||||||
historyLimit int
|
historyLimit int
|
||||||
@ -200,6 +202,8 @@ func (c *Client) executor(cmd string) {
|
|||||||
c.settings()
|
c.settings()
|
||||||
case "pretty":
|
case "pretty":
|
||||||
c.togglePretty()
|
c.togglePretty()
|
||||||
|
case "scientific":
|
||||||
|
c.toggleScientific()
|
||||||
case "use":
|
case "use":
|
||||||
c.use(cmdArgs)
|
c.use(cmdArgs)
|
||||||
case "insert":
|
case "insert":
|
||||||
@ -367,9 +371,10 @@ func (c *Client) runAndShowQuery(query string) {
|
|||||||
|
|
||||||
func (c *Client) help() {
|
func (c *Client) help() {
|
||||||
fmt.Println(`Usage:
|
fmt.Println(`Usage:
|
||||||
pretty toggles pretty print for the json format
|
|
||||||
use <db_name> sets current database
|
use <db_name> sets current database
|
||||||
format <format> specifies the format of the server responses: json, csv, column, table
|
format <format> specifies the format of the server responses: json, csv, column, table
|
||||||
|
pretty toggles pretty print for the json format
|
||||||
|
scientific toggles scientific numeric format for table format
|
||||||
precision <format> specifies the format of the timestamp: rfc3339, h, m, s, ms, u or ns
|
precision <format> specifies the format of the timestamp: rfc3339, h, m, s, ms, u or ns
|
||||||
history displays command history
|
history displays command history
|
||||||
settings outputs the current settings for the shell
|
settings outputs the current settings for the shell
|
||||||
@ -403,6 +408,7 @@ func (c *Client) settings() {
|
|||||||
fmt.Fprintf(w, "Database\t%s\n", c.Database)
|
fmt.Fprintf(w, "Database\t%s\n", c.Database)
|
||||||
fmt.Fprintf(w, "RetentionPolicy\t%s\n", c.RetentionPolicy)
|
fmt.Fprintf(w, "RetentionPolicy\t%s\n", c.RetentionPolicy)
|
||||||
fmt.Fprintf(w, "Pretty\t%v\n", c.Pretty)
|
fmt.Fprintf(w, "Pretty\t%v\n", c.Pretty)
|
||||||
|
fmt.Fprintf(w, "Scientific\t%v\n", c.Scientific)
|
||||||
fmt.Fprintf(w, "Format\t%s\n", c.Format)
|
fmt.Fprintf(w, "Format\t%s\n", c.Format)
|
||||||
fmt.Fprintf(w, "Precision\t%s\n", c.Precision)
|
fmt.Fprintf(w, "Precision\t%s\n", c.Precision)
|
||||||
fmt.Fprintln(w)
|
fmt.Fprintln(w)
|
||||||
@ -640,6 +646,8 @@ outer:
|
|||||||
}
|
}
|
||||||
allSeries := res.GetSeries()
|
allSeries := res.GetSeries()
|
||||||
for seriesIdx < len(allSeries) {
|
for seriesIdx < len(allSeries) {
|
||||||
|
termenv.AltScreen()
|
||||||
|
defer termenv.ExitAltScreen()
|
||||||
series := allSeries[seriesIdx]
|
series := allSeries[seriesIdx]
|
||||||
p := tea.NewProgram(NewModel(series,
|
p := tea.NewProgram(NewModel(series,
|
||||||
jumpToLastPage,
|
jumpToLastPage,
|
||||||
@ -648,7 +656,9 @@ outer:
|
|||||||
resIdx+1,
|
resIdx+1,
|
||||||
len(allResults),
|
len(allResults),
|
||||||
seriesIdx+1,
|
seriesIdx+1,
|
||||||
len(allSeries)))
|
len(allSeries),
|
||||||
|
c.Scientific),
|
||||||
|
)
|
||||||
model, err := p.StartReturningModel()
|
model, err := p.StartReturningModel()
|
||||||
jumpToLastPage = false
|
jumpToLastPage = false
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -688,6 +698,11 @@ func (c *Client) togglePretty() {
|
|||||||
color.HiBlack("Pretty: %v", c.Pretty)
|
color.HiBlack("Pretty: %v", c.Pretty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) toggleScientific() {
|
||||||
|
c.Scientific = !c.Scientific
|
||||||
|
color.HiBlack("Scientific: %v", c.Scientific)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) use(args []string) {
|
func (c *Client) use(args []string) {
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
color.Red("wrong number of args for \"use <database>\"")
|
color.Red("wrong number of args for \"use <database>\"")
|
||||||
|
2
go.mod
2
go.mod
@ -18,6 +18,7 @@ require (
|
|||||||
github.com/influxdata/go-prompt v0.2.7
|
github.com/influxdata/go-prompt v0.2.7
|
||||||
github.com/influxdata/influxdb/v2 v2.3.0
|
github.com/influxdata/influxdb/v2 v2.3.0
|
||||||
github.com/mattn/go-isatty v0.0.14
|
github.com/mattn/go-isatty v0.0.14
|
||||||
|
github.com/muesli/termenv v0.12.0
|
||||||
github.com/olekukonko/tablewriter v0.0.5
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/urfave/cli v1.22.5
|
github.com/urfave/cli v1.22.5
|
||||||
@ -44,7 +45,6 @@ require (
|
|||||||
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect
|
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect
|
||||||
github.com/muesli/cancelreader v0.2.0 // indirect
|
github.com/muesli/cancelreader v0.2.0 // indirect
|
||||||
github.com/muesli/reflow v0.3.0 // indirect
|
github.com/muesli/reflow v0.3.0 // indirect
|
||||||
github.com/muesli/termenv v0.12.0 // indirect
|
|
||||||
github.com/pkg/term v1.2.0-beta.2 // indirect
|
github.com/pkg/term v1.2.0-beta.2 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
|
Reference in New Issue
Block a user