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:
Andrew Lee
2022-07-01 10:22:13 -06:00
committed by GitHub
parent da2899d71d
commit b9ffcb4b5a
4 changed files with 54 additions and 15 deletions

View File

@ -120,6 +120,7 @@ func (c *Client) completer(d prompt.Document) []prompt.Suggest {
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",

View File

@ -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{}
if m.name != "" {
fmt.Printf("Name: %s\n", color.GreenString(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:

View File

@ -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
View File

@ -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