Add support for the TUI displaying custom columns

This commit is contained in:
David Dworken 2022-10-26 22:11:07 -07:00
parent 10d6c97a50
commit 16fa64b7a7
2 changed files with 77 additions and 32 deletions

View File

@ -397,7 +397,7 @@ func AddToDbIfNew(db *gorm.DB, entry data.HistoryEntry) {
} }
} }
func getCustomColumnValue(ctx *context.Context, header string, entry *data.HistoryEntry) (string, error) { func getCustomColumnValue(ctx *context.Context, header string, entry data.HistoryEntry) (string, error) {
for _, c := range entry.CustomColumns { for _, c := range entry.CustomColumns {
if strings.EqualFold(c.Name, header) { if strings.EqualFold(c.Name, header) {
return c.Val, nil return c.Val, nil
@ -412,6 +412,41 @@ func getCustomColumnValue(ctx *context.Context, header string, entry *data.Histo
return "", fmt.Errorf("failed to find a column matching the column name %#v (is there a typo?)", header) return "", fmt.Errorf("failed to find a column matching the column name %#v (is there a typo?)", header)
} }
func buildTableRow(ctx *context.Context, columnNames []string, entry data.HistoryEntry) ([]string, error) {
row := make([]string, 0)
for _, header := range columnNames {
switch header {
case "Hostname":
row = append(row, entry.Hostname)
case "CWD":
row = append(row, entry.CurrentWorkingDirectory)
case "Timestamp":
row = append(row, entry.StartTime.Format("Jan 2 2006 15:04:05 MST"))
case "Runtime":
row = append(row, entry.EndTime.Sub(entry.StartTime).Round(time.Millisecond).String())
case "Exit Code":
row = append(row, fmt.Sprintf("%d", entry.ExitCode))
case "Command":
row = append(row, entry.Command)
default:
customColumnValue, err := getCustomColumnValue(ctx, header, entry)
if err != nil {
return nil, err
}
row = append(row, customColumnValue)
}
}
return row, nil
}
func stringArrayToAnyArray(arr []string) []any {
ret := make([]any, 0)
for _, item := range arr {
ret = append(ret, item)
}
return ret
}
func DisplayResults(ctx *context.Context, results []*data.HistoryEntry) error { func DisplayResults(ctx *context.Context, results []*data.HistoryEntry) error {
config := hctx.GetConf(ctx) config := hctx.GetConf(ctx)
headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc() headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc()
@ -424,30 +459,11 @@ func DisplayResults(ctx *context.Context, results []*data.HistoryEntry) error {
tbl.WithHeaderFormatter(headerFmt) tbl.WithHeaderFormatter(headerFmt)
for _, result := range results { for _, result := range results {
row := make([]any, 0) row, err := buildTableRow(ctx, config.DisplayedColumns, *result)
for _, header := range config.DisplayedColumns {
switch header {
case "Hostname":
row = append(row, result.Hostname)
case "CWD":
row = append(row, result.CurrentWorkingDirectory)
case "Timestamp":
row = append(row, result.StartTime.Format("Jan 2 2006 15:04:05 MST"))
case "Runtime":
row = append(row, result.EndTime.Sub(result.StartTime).Round(time.Millisecond).String())
case "Exit Code":
row = append(row, fmt.Sprintf("%d", result.ExitCode))
case "Command":
row = append(row, result.Command)
default:
customColumnValue, err := getCustomColumnValue(ctx, header, result)
if err != nil { if err != nil {
return err return err
} }
row = append(row, customColumnValue) tbl.AddRow(stringArrayToAnyArray(row))
}
}
tbl.AddRow(row...)
} }
tbl.Print() tbl.Print()

View File

@ -95,7 +95,7 @@ func (m model) Init() tea.Cmd {
func runQueryAndUpdateTable(m model) model { func runQueryAndUpdateTable(m model) model {
if m.runQuery != nil && *m.runQuery != m.lastQuery { if m.runQuery != nil && *m.runQuery != m.lastQuery {
rows, numEntries, err := getRows(m.ctx, *m.runQuery, PADDED_NUM_ENTRIES) rows, numEntries, err := getRows(m.ctx, hctx.GetConf(m.ctx).DisplayedColumns, *m.runQuery, PADDED_NUM_ENTRIES)
if err != nil { if err != nil {
m.searchErr = err m.searchErr = err
return m return m
@ -169,7 +169,18 @@ func (m model) View() string {
return fmt.Sprintf("An unrecoverable error occured: %v\n", m.err) return fmt.Sprintf("An unrecoverable error occured: %v\n", m.err)
} }
if m.selected { if m.selected {
selectedRow = m.table.SelectedRow()[4] indexOfCommand := -1
for i, columnName := range hctx.GetConf(m.ctx).DisplayedColumns {
if columnName == "Command" {
indexOfCommand = i
break
}
}
if indexOfCommand == -1 {
selectedRow = "Error: Table doesn't have a column named `Command`?"
return ""
}
selectedRow = m.table.SelectedRow()[indexOfCommand]
return "" return ""
} }
loadingMessage := "" loadingMessage := ""
@ -186,7 +197,7 @@ func (m model) View() string {
return fmt.Sprintf("\n%s\n%s%s\nSearch Query: %s\n\n%s\n", loadingMessage, warning, m.banner, m.queryInput.View(), baseStyle.Render(m.table.View())) return fmt.Sprintf("\n%s\n%s%s\nSearch Query: %s\n\n%s\n", loadingMessage, warning, m.banner, m.queryInput.View(), baseStyle.Render(m.table.View()))
} }
func getRows(ctx *context.Context, query string, numEntries int) ([]table.Row, int, error) { func getRows(ctx *context.Context, columnNames []string, query string, numEntries int) ([]table.Row, int, error) {
db := hctx.GetDb(ctx) db := hctx.GetDb(ctx)
data, err := data.Search(db, query, numEntries) data, err := data.Search(db, query, numEntries)
if err != nil { if err != nil {
@ -197,7 +208,10 @@ func getRows(ctx *context.Context, query string, numEntries int) ([]table.Row, i
if i < len(data) { if i < len(data) {
entry := data[i] entry := data[i]
entry.Command = strings.ReplaceAll(entry.Command, "\n", " ") // TODO: handle multi-line commands better here entry.Command = strings.ReplaceAll(entry.Command, "\n", " ") // TODO: handle multi-line commands better here
row := table.Row{entry.Hostname, entry.CurrentWorkingDirectory, entry.StartTime.Format("Jan 2 2006 15:04:05 MST"), fmt.Sprintf("%d", entry.ExitCode), entry.Command} row, err := buildTableRow(ctx, columnNames, *entry)
if err != nil {
return nil, 0, fmt.Errorf("failed to build row for entry=%#v: %v", entry, err)
}
rows = append(rows, row) rows = append(rows, row)
} else { } else {
rows = append(rows, table.Row{}) rows = append(rows, table.Row{})
@ -230,7 +244,7 @@ func makeTableColumns(ctx *context.Context, columnNames []string, rows []table.R
// Calculate the maximum column width that is useful for each column if we search for the empty string // Calculate the maximum column width that is useful for each column if we search for the empty string
if bigQueryResults == nil { if bigQueryResults == nil {
bigRows, _, err := getRows(ctx, "", 1000) bigRows, _, err := getRows(ctx, columnNames, "", 1000)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -243,7 +257,7 @@ func makeTableColumns(ctx *context.Context, columnNames []string, rows []table.R
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get terminal size: %v", err) return nil, fmt.Errorf("failed to get terminal size: %v", err)
} }
for totalWidth < terminalWidth { for totalWidth < (terminalWidth - len(columnNames)) {
prevTotalWidth := totalWidth prevTotalWidth := totalWidth
for i := range columnNames { for i := range columnNames {
if columnWidths[i] < maximumColumnWidths[i]+5 { if columnWidths[i] < maximumColumnWidths[i]+5 {
@ -256,6 +270,20 @@ func makeTableColumns(ctx *context.Context, columnNames []string, rows []table.R
} }
} }
// And if we are too large from the initial query, let's shrink things to make the table fit. We'll use the heuristic of always shrinking the widest column.
for totalWidth > terminalWidth {
largestColumnIdx := -1
largestColumnSize := -1
for i := range columnNames {
if columnWidths[i] > largestColumnSize {
largestColumnIdx = i
largestColumnSize = columnWidths[i]
}
}
columnWidths[largestColumnIdx] -= 2
totalWidth -= 2
}
// And finally, create some actual columns! // And finally, create some actual columns!
columns := make([]table.Column, 0) columns := make([]table.Column, 0)
for i, name := range columnNames { for i, name := range columnNames {
@ -272,7 +300,8 @@ func max(a, b int) int {
} }
func makeTable(ctx *context.Context, rows []table.Row) (table.Model, error) { func makeTable(ctx *context.Context, rows []table.Row) (table.Model, error) {
columns, err := makeTableColumns(ctx, []string{"Hostname", "CWD", "Timestamp", "Exit Code", "Command"}, rows) config := hctx.GetConf(ctx)
columns, err := makeTableColumns(ctx, config.DisplayedColumns, rows)
if err != nil { if err != nil {
return table.Model{}, err return table.Model{}, err
} }
@ -327,7 +356,7 @@ func makeTable(ctx *context.Context, rows []table.Row) (table.Model, error) {
func TuiQuery(ctx *context.Context, gitCommit, initialQuery string) error { func TuiQuery(ctx *context.Context, gitCommit, initialQuery string) error {
lipgloss.SetColorProfile(termenv.ANSI) lipgloss.SetColorProfile(termenv.ANSI)
rows, numEntries, err := getRows(ctx, initialQuery, PADDED_NUM_ENTRIES) rows, numEntries, err := getRows(ctx, hctx.GetConf(ctx).DisplayedColumns, initialQuery, PADDED_NUM_ENTRIES)
if err != nil { if err != nil {
return err return err
} }