From 82f819d369f46a3b333bcf68930736ece1323806 Mon Sep 17 00:00:00 2001 From: David Dworken Date: Tue, 6 Feb 2024 20:04:21 -0800 Subject: [PATCH] Fix handling of new lines in commands for #163 (#170) * Fix handling of new lines in commands for #163 * Move code for table from lib.go to query.go * Update goldens --- client/cmd/query.go | 52 +++++++++++++++++- client/lib/lib.go | 53 +------------------ .../testControlR-DisplayMultiline-zsh | 48 ++++++++--------- client/tui/tui.go | 15 ++++-- 4 files changed, 88 insertions(+), 80 deletions(-) diff --git a/client/cmd/query.go b/client/cmd/query.go index 8634ae1..f399460 100644 --- a/client/cmd/query.go +++ b/client/cmd/query.go @@ -7,10 +7,13 @@ import ( "os" "strings" + "github.com/ddworken/hishtory/client/data" "github.com/ddworken/hishtory/client/hctx" "github.com/ddworken/hishtory/client/lib" "github.com/ddworken/hishtory/client/tui" + "github.com/fatih/color" "github.com/muesli/termenv" + "github.com/rodaine/table" "github.com/spf13/cobra" ) @@ -148,7 +151,54 @@ func query(ctx context.Context, query string) { numResults := 25 data, err := lib.Search(ctx, db, query, numResults*5) lib.CheckFatalError(err) - lib.CheckFatalError(lib.DisplayResults(ctx, data, numResults)) + lib.CheckFatalError(DisplayResults(ctx, data, numResults)) +} + +func DisplayResults(ctx context.Context, results []*data.HistoryEntry, numResults int) error { + config := hctx.GetConf(ctx) + headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc() + + columns := make([]any, 0) + for _, c := range config.DisplayedColumns { + columns = append(columns, c) + } + tbl := table.New(columns...) + tbl.WithHeaderFormatter(headerFmt) + + numRows := 0 + + var seenCommands = make(map[string]bool) + + for _, entry := range results { + if config.FilterDuplicateCommands && entry != nil { + cmd := strings.TrimSpace(entry.Command) + if seenCommands[cmd] { + continue + } + seenCommands[cmd] = true + } + + row, err := lib.BuildTableRow(ctx, config.DisplayedColumns, *entry, func(s string) string { return s }) + if err != nil { + return err + } + tbl.AddRow(stringArrayToAnyArray(row)...) + numRows += 1 + if numRows >= numResults { + break + } + } + + tbl.Print() + return nil +} + +func stringArrayToAnyArray(arr []string) []any { + ret := make([]any, 0) + for _, item := range arr { + ret = append(ret, item) + } + return ret } func displayBannerIfSet(ctx context.Context) error { diff --git a/client/lib/lib.go b/client/lib/lib.go index 4d3851b..773026d 100644 --- a/client/lib/lib.go +++ b/client/lib/lib.go @@ -27,9 +27,7 @@ import ( "gorm.io/gorm" "github.com/araddon/dateparse" - "github.com/fatih/color" "github.com/google/uuid" - "github.com/rodaine/table" "github.com/schollz/progressbar/v3" "github.com/ddworken/hishtory/client/data" @@ -85,7 +83,7 @@ func getCustomColumnValue(ctx context.Context, header string, entry data.History 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) { +func BuildTableRow(ctx context.Context, columnNames []string, entry data.HistoryEntry, commandRenderer func(string) string) ([]string, error) { row := make([]string, 0) for _, header := range columnNames { switch header { @@ -109,7 +107,7 @@ func BuildTableRow(ctx context.Context, columnNames []string, entry data.History case "Exit Code", "Exit_Code", "ExitCode", "exitcode": row = append(row, fmt.Sprintf("%d", entry.ExitCode)) case "Command", "command": - row = append(row, entry.Command) + row = append(row, commandRenderer(entry.Command)) case "User", "user": row = append(row, entry.LocalUsername) default: @@ -138,53 +136,6 @@ func MakeRegexFromQuery(query string) string { return r } -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, numResults int) error { - config := hctx.GetConf(ctx) - headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc() - - columns := make([]any, 0) - for _, c := range config.DisplayedColumns { - columns = append(columns, c) - } - tbl := table.New(columns...) - tbl.WithHeaderFormatter(headerFmt) - - numRows := 0 - - var seenCommands = make(map[string]bool) - - for _, entry := range results { - if config.FilterDuplicateCommands && entry != nil { - cmd := strings.TrimSpace(entry.Command) - if seenCommands[cmd] { - continue - } - seenCommands[cmd] = true - } - - row, err := BuildTableRow(ctx, config.DisplayedColumns, *entry) - if err != nil { - return err - } - tbl.AddRow(stringArrayToAnyArray(row)...) - numRows += 1 - if numRows >= numResults { - break - } - } - - tbl.Print() - return nil -} - func CheckFatalError(err error) { if err != nil { _, filename, line, _ := runtime.Caller(1) diff --git a/client/testdata/testControlR-DisplayMultiline-zsh b/client/testdata/testControlR-DisplayMultiline-zsh index 215384d..392b605 100644 --- a/client/testdata/testControlR-DisplayMultiline-zsh +++ b/client/testdata/testControlR-DisplayMultiline-zsh @@ -1,27 +1,27 @@ Search Query: > Slah -┌─────────────────────────────────────────────────────────────────────────────┐ -│ Hostname Exit Code Command foo │ -│─────────────────────────────────────────────────────────────────────────────│ -│ ghaction-runner-hostname 0 ls \\n-Slah \\n/ foo │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────┘ +┌───────────────────────────────────────────────────────────────────────────────┐ +│ Hostname Exit Code Command foo │ +│───────────────────────────────────────────────────────────────────────────────│ +│ ghaction-runner-hostname 0 "ls \\\n-Slah \\\n/" foo │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +└───────────────────────────────────────────────────────────────────────────────┘ hiSHtory: Search your shell history • ctrl+h help \ No newline at end of file diff --git a/client/tui/tui.go b/client/tui/tui.go index b4b134d..3d18bb3 100644 --- a/client/tui/tui.go +++ b/client/tui/tui.go @@ -515,7 +515,7 @@ func getRowsFromAiSuggestions(ctx context.Context, columnNames []string, query s EntryId: "OpenAI", } entries = append(entries, &entry) - row, err := lib.BuildTableRow(ctx, columnNames, entry) + row, err := lib.BuildTableRow(ctx, columnNames, entry, func(s string) string { return s }) if err != nil { return nil, nil, fmt.Errorf("failed to build row for entry=%#v: %w", entry, err) } @@ -551,8 +551,7 @@ func getRows(ctx context.Context, columnNames []string, defaultFilter, query str seenCommands[cmd] = true } - entry.Command = strings.ReplaceAll(entry.Command, "\n", "\\n") - row, err := lib.BuildTableRow(ctx, columnNames, *entry) + row, err := lib.BuildTableRow(ctx, columnNames, *entry, commandEscaper) if err != nil { return nil, nil, fmt.Errorf("failed to build row for entry=%#v: %w", entry, err) } @@ -565,6 +564,14 @@ func getRows(ctx context.Context, columnNames []string, defaultFilter, query str return rows, filteredData, nil } +func commandEscaper(cmd string) string { + if !strings.Contains(cmd, "\n") { + // No special escaping necessary + return cmd + } + return fmt.Sprintf("%#v", cmd) +} + func calculateColumnWidths(rows []table.Row, numColumns int) []int { neededColumnWidth := make([]int, numColumns) for _, row := range rows { @@ -935,7 +942,7 @@ func TuiQuery(ctx context.Context, initialQuery string) error { // Print out the initialQuery instead so that we don't clear the terminal SELECTED_COMMAND = initialQuery } - fmt.Printf("%s\n", strings.ReplaceAll(SELECTED_COMMAND, "\\n", "\n")) + fmt.Printf("%s\n", SELECTED_COMMAND) return nil }