Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pretty table format to REPL #393

Merged
merged 45 commits into from
Jun 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
063f191
add v1-compatible query path and refactor other paths to de-duplicate…
candrewlee14 May 20, 2022
9717d7a
add initial influxQL repl
candrewlee14 May 20, 2022
0487778
add ping endpoint to schema
candrewlee14 May 20, 2022
cf69fce
improve prompt UX, implement some commands
candrewlee14 May 20, 2022
acd8e6d
fix json column type in schema and improve completion
candrewlee14 May 21, 2022
2133d2a
feat: add table formatter and move to forked go-prompt
candrewlee14 May 21, 2022
04e1594
improve formatting and add table pagination
candrewlee14 May 23, 2022
340d087
implement more REPL commands, including insert and history
candrewlee14 May 23, 2022
675b846
implement "INSERT INTO"
candrewlee14 May 24, 2022
d7b3e52
move repl command to "v1 repl"
candrewlee14 May 24, 2022
6febf46
refactor and improve documentation
candrewlee14 May 25, 2022
444d023
clean up v1_repl cmd
candrewlee14 May 25, 2022
3f5f6b8
update to latest openapi, use some openapi paths instead of overrides
candrewlee14 May 31, 2022
4c1dd75
remove additional files that were moved to openapi
candrewlee14 May 31, 2022
1242c70
compute historyFilePath at REPL start
candrewlee14 May 31, 2022
6a77885
clean up REPL use command logic flow
candrewlee14 May 31, 2022
b36d6c7
clean up comments for TODOs now in issues
candrewlee14 Jun 1, 2022
2ee1521
move gopher (chonky boi)
candrewlee14 Jun 1, 2022
55e2593
remove autocompletion for separate PR
candrewlee14 Jun 1, 2022
fb84335
run go mod tidy
candrewlee14 Jun 1, 2022
7083089
add rfc3339 precision option
candrewlee14 Jun 1, 2022
9bb6a34
allow left and right column scrolling to display whole table
candrewlee14 Jun 2, 2022
caa0c33
add error to JSON query response
candrewlee14 Jun 2, 2022
a772073
add tags and partial to JSON response series schema
candrewlee14 Jun 2, 2022
5be4302
fix csv formatting and add column formatting
candrewlee14 Jun 2, 2022
91703b9
remove table format for separate PR
candrewlee14 Jun 3, 2022
eacf623
add pretty table format to REPL
candrewlee14 Jun 3, 2022
89d2fed
fix getDatabases
candrewlee14 Jun 3, 2022
eee900c
move from write to legacy write endpoint for INSERT
candrewlee14 Jun 7, 2022
d2b208f
remove history vestiges
candrewlee14 Jun 7, 2022
1475e75
allow multiple spaces in INSERT commands
candrewlee14 Jun 7, 2022
f275155
add precision comment
candrewlee14 Jun 7, 2022
cf58bb9
remove auth for separate PR
candrewlee14 Jun 7, 2022
672457f
separate parseInsert and add unit test
candrewlee14 Jun 7, 2022
8fb6500
add additional test case and improve error messages
candrewlee14 Jun 9, 2022
453053e
fix missing errors import
candrewlee14 Jun 9, 2022
bb57d2b
Merge branch '331-add-back-the-influxql-repl' into add-repl-table-format
candrewlee14 Jun 10, 2022
cb1b02e
update for simpler horizontal scrolling in table mode
candrewlee14 Jun 12, 2022
6d668b0
Merge branch 'main' into add-repl-table-format
candrewlee14 Jun 14, 2022
7da4841
improve dialog printing
candrewlee14 Jun 14, 2022
9d888f0
Merge 'main' with autocomplete into add-repl-table-format
candrewlee14 Jun 14, 2022
f3d942c
improve table format and interactions
candrewlee14 Jun 24, 2022
e3ff8eb
jump to first page on shift-up
candrewlee14 Jun 24, 2022
7486eb4
add keybinding info
candrewlee14 Jun 24, 2022
7b50b80
change wording from result to table, flip
candrewlee14 Jun 24, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clients/v1_shell/autocomplete.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ func (c *Client) completer(d prompt.Document) []prompt.Suggest {
"column",
"csv",
"json",
"table",
)},
"SELECT": {subsuggestFn: c.suggestSelect},
"INSERT": {},
Expand Down
231 changes: 231 additions & 0 deletions clients/v1_shell/table_model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
package v1shell

import (
"fmt"
"os"
"strings"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/evertras/bubble-table/table"
"github.com/fatih/color"
"github.com/influxdata/influx-cli/v2/api"
"github.com/mattn/go-isatty"
)

const (
colPadding int = 2
)

type EndingStatus uint

const (
quitStatus EndingStatus = iota
goToPrevTableStatus
goToPrevTableJumpFirstPageStatus
goToNextTableStatus
)

type Model struct {
name string
tags map[string]string
curResult int
resultMax int
curSeries int
seriesMax int
rows []table.Row
allCols []table.Column
colNames []string
simpleTable table.Model
totalWidth int
endingStatus EndingStatus
tableScreenPadding int
}

func NewModel(
res api.InfluxqlJsonResponseSeries,
jumpToFirstPage bool,
name string,
tags map[string]string,
curRes int,
resMax int,
curSer int,
serMax int) Model {

cols := make([]table.Column, len(*res.Columns)+1)
colWidths := make([]int, len(*res.Columns)+1)
rows := make([]table.Row, len(*res.Values))
colNames := *res.Columns
for rowI, row := range *res.Values {
rd := table.RowData{}
rd["index"] = fmt.Sprintf("%d", rowI+1)
colWidths[0] = len("index") + colPadding
for colI, rowVal := range row {
var item string
switch val := rowVal.(type) {
case int:
item = fmt.Sprintf("%d", val)
case string:
item = fmt.Sprintf("%q", val)
default:
item = fmt.Sprintf("%v", val)
}
rd[colNames[colI]] = item
if colWidths[colI+1] < len(item)+colPadding {
colWidths[colI+1] = len(item) + colPadding
}
}
rows[rowI] = table.NewRow(rd).
WithStyle(lipgloss.NewStyle().Align(lipgloss.Center))
}
cols[0] = table.NewColumn("index", "index", colWidths[0])
indexStyle := lipgloss.NewStyle()
if isatty.IsTerminal(os.Stdout.Fd()) {
indexStyle = indexStyle.
Faint(true).
Align(lipgloss.Center)
}
cols[0] = cols[0].WithStyle(indexStyle)
for colI, colTitle := range colNames {
if colWidths[colI+1] < len(colTitle)+colPadding {
colWidths[colI+1] = len(colTitle) + colPadding
}
cols[colI+1] = table.NewColumn(colTitle, color.HiCyanString(colTitle), colWidths[colI+1]).
WithStyle(lipgloss.NewStyle().Align(lipgloss.Center))
}
colNames = append([]string{"index"}, colNames...)
screenPadding := 10
if len(tags) > 0 {
screenPadding++
}
m := Model{
name: name,
tags: tags,
curResult: curRes,
resultMax: resMax,
curSeries: curSer,
seriesMax: serMax,
rows: rows,
allCols: cols,
colNames: colNames,
tableScreenPadding: screenPadding,
}
keybind := table.DefaultKeyMap()
keybind.RowUp.SetEnabled(false)
keybind.RowDown.SetEnabled(false)
keybind.PageUp.SetEnabled(false)
keybind.PageDown.SetEnabled(false)
keybind.ScrollLeft.SetKeys("left")
keybind.ScrollRight.SetKeys("right")
keybind.Filter.Unbind()
keybind.FilterBlur.Unbind()
keybind.FilterClear.Unbind()

m.simpleTable = table.New(m.allCols).
WithRows(m.rows).
WithPageSize(15).
WithMaxTotalWidth(500).
WithHorizontalFreezeColumnCount(1).
WithStaticFooter(
fmt.Sprintf("%d Columns, %d Rows, Page %d/%d",
len(m.allCols), len(m.rows), m.simpleTable.CurrentPage(), m.simpleTable.MaxPages())).
HighlightStyle(lipgloss.NewStyle()).
Focused(true).
SelectableRows(false).
WithKeyMap(keybind)

if jumpToFirstPage {
m.simpleTable = m.simpleTable.PageLast()
}
return m
}

func (m Model) Init() tea.Cmd {
color.Magenta("Interactive Table View (press q to exit mode, shift+up/down to navigate tables):")
builder := strings.Builder{}
fmt.Printf("Name: %s\n", color.GreenString(m.name))
if len(m.tags) > 0 {
fmt.Print("Tags: ")
for key, val := range m.tags {
if key == "" || val == "" {
continue
}
builder.WriteString(fmt.Sprintf("%s=%s, ", color.YellowString(key), color.CyanString(val)))
}
tagline := builder.String()
fmt.Print(tagline[:len(tagline)-2])
fmt.Println("")
}
return nil
}

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var (
cmd tea.Cmd
cmds []tea.Cmd
)

m.simpleTable, cmd = m.simpleTable.Update(msg)
cmds = append(cmds, cmd)

switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "ctrl+d", "esc", "q":
m.simpleTable = m.simpleTable.Focused(false)
fmt.Printf("\n")
m.endingStatus = quitStatus
cmds = append(cmds, tea.Quit)
case "shift+up":
if !(m.curResult == 1 && m.curSeries == 1) {
m.endingStatus = goToPrevTableJumpFirstPageStatus
cmds = append(cmds, tea.Quit)
}
case "shift+down":
if !(m.curResult == m.resultMax && m.curSeries == m.seriesMax) {
m.endingStatus = goToNextTableStatus
cmds = append(cmds, tea.Quit)
}
case "up":
if m.simpleTable.CurrentPage() == 1 {
if !(m.curResult == 1 && m.curSeries == 1) {
m.endingStatus = goToPrevTableStatus
cmds = append(cmds, tea.Quit)
}
} else {
m.simpleTable = m.simpleTable.PageUp()
}
case "down":
if m.simpleTable.CurrentPage() == m.simpleTable.MaxPages() {
if !(m.curResult == m.resultMax && m.curSeries == m.seriesMax) {
m.endingStatus = goToNextTableStatus
cmds = append(cmds, tea.Quit)
}
} else {
m.simpleTable = m.simpleTable.PageDown()
}
case "<":
m.simpleTable = m.simpleTable.PageFirst()
case ">":
m.simpleTable = m.simpleTable.PageLast()
}
case tea.WindowSizeMsg:
m.totalWidth = msg.Width
m.simpleTable = m.simpleTable.WithPageSize(msg.Height - m.tableScreenPadding).
WithMaxTotalWidth(msg.Width)
}
m.simpleTable = m.simpleTable.WithStaticFooter(
fmt.Sprintf("%d Columns, %d Rows, Page %d/%d\nTable %d/%d, Statement %d/%d",
len(m.allCols), len(m.rows), m.simpleTable.CurrentPage(), m.simpleTable.MaxPages(),
m.curSeries, m.seriesMax,
m.curResult, m.resultMax))
return m, tea.Batch(cmds...)
}

func (m Model) View() string {
body := strings.Builder{}

body.WriteString(m.simpleTable.View())
body.WriteString("\n")
return body.String()
}
Loading