mirror of
https://github.com/ddworken/hishtory.git
synced 2024-11-23 08:45:16 +01:00
Add support for the TUI displaying custom columns
This commit is contained in:
parent
10d6c97a50
commit
16fa64b7a7
@ -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 {
|
if err != nil {
|
||||||
switch header {
|
return err
|
||||||
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 {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
row = append(row, customColumnValue)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
tbl.AddRow(row...)
|
tbl.AddRow(stringArrayToAnyArray(row))
|
||||||
}
|
}
|
||||||
|
|
||||||
tbl.Print()
|
tbl.Print()
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user