diff --git a/client/client_test.go b/client/client_test.go index 8488f75..7b848f0 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -2165,6 +2165,14 @@ func testTui_search(t *testing.T, onlineStatus OnlineStatus) { "'\"'foo:bar'\"'", })) testutils.CompareGoldens(t, out, "TestTui-SearchColonDoubleQuoted") + + // And check that we can quote dashes + require.NoError(t, db.Create(testutils.MakeFakeHistoryEntry("foo --bar")).Error) + out = stripTuiCommandPrefix(t, captureTerminalOutput(t, tester, []string{ + "hishtory SPACE tquery ENTER", + "'\"'--bar'\"'", + })) + testutils.CompareGoldens(t, out, "TestTui-SearchQuoteDash") } func testTui_general(t *testing.T, onlineStatus OnlineStatus) { @@ -2520,8 +2528,8 @@ echo bar`) tester.RunInteractiveShell(t, `hishtory config-set displayed-columns 'Exit Code' git_remote Command`) out = tester.RunInteractiveShell(t, `hishtory query -pipefail`) testutils.CompareGoldens(t, out, fmt.Sprintf("testCustomColumns-query-isAction=%v", testutils.IsGithubAction())) - out = captureTerminalOutput(t, tester, []string{"hishtory SPACE tquery SPACE -pipefail ENTER"}) - out = stripRequiredPrefix(t, out, "hishtory tquery -pipefail") + out = captureTerminalOutput(t, tester, []string{"hishtory SPACE tquery SPACE ENTER", "-pipefail"}) + out = stripRequiredPrefix(t, out, "hishtory tquery") testName := "testCustomColumns-tquery-" + tester.ShellName() if testutils.IsGithubAction() { testName += "-isAction" @@ -2778,8 +2786,8 @@ func TestTimestampFormat(t *testing.T) { // And check that it is displayed in both the tui and the classic view out := hishtoryQuery(t, tester, "-pipefail -tablesizing") testutils.CompareGoldens(t, out, "TestTimestampFormat-query") - out = captureTerminalOutput(t, tester, []string{"hishtory SPACE tquery SPACE -pipefail SPACE -tablesizing ENTER"}) - out = stripRequiredPrefix(t, out, "hishtory tquery -pipefail -tablesizing") + out = captureTerminalOutput(t, tester, []string{"hishtory SPACE tquery ENTER", "table_cmd SPACE -tquery"}) + out = stripRequiredPrefix(t, out, "hishtory tquery") testutils.CompareGoldens(t, out, "TestTimestampFormat-tquery") } @@ -2818,7 +2826,7 @@ func TestSortByConsistentTimezone(t *testing.T) { testutils.CompareGoldens(t, out, "TestSortByConsistentTimezone-query") out = tester.RunInteractiveShell(t, `hishtory export -pipefail -tablesizing`) testutils.CompareGoldens(t, out, "TestSortByConsistentTimezone-export") - out = captureTerminalOutput(t, tester, []string{"hishtory SPACE tquery SPACE -pipefail SPACE -tablesizing ENTER"}) + out = captureTerminalOutput(t, tester, []string{"hishtory SPACE tquery ENTER", "-pipefail SPACE -tablesizing"}) out = stripTuiCommandPrefix(t, out) require.Regexp(t, regexp.MustCompile(`Timestamp[\s\S]*Command[\s\S]*Apr 16 2022 01:36:26 PDT[\s\S]*third_entry[\s\S]*Apr 16 2022 01:19:46 PDT[\s\S]*second_entry[\s\S]*Apr 16 2022 01:03:06 PDT[\s\S]*first_entry`), out) } @@ -2879,8 +2887,8 @@ echo foo`) tester.RunInteractiveShell(t, `hishtory config-set displayed-columns 'Exit Code' Command`) out = tester.RunInteractiveShell(t, `hishtory query -pipefail`) testutils.CompareGoldens(t, out, "testRemoveDuplicateRows-query") - out = captureTerminalOutput(t, tester, []string{"hishtory SPACE tquery SPACE -pipefail ENTER"}) - out = stripRequiredPrefix(t, out, "hishtory tquery -pipefail") + out = captureTerminalOutput(t, tester, []string{"hishtory SPACE tquery ENTER", "-pipefail"}) + out = stripRequiredPrefix(t, out, "hishtory tquery") testutils.CompareGoldens(t, out, "testRemoveDuplicateRows-tquery") // And change the config to filter out duplicate rows @@ -2895,18 +2903,20 @@ echo foo`) testutils.CompareGoldens(t, out, "testRemoveDuplicateRows-enabled-query") // Check tquery - out = captureTerminalOutput(t, tester, []string{"hishtory SPACE tquery SPACE -pipefail ENTER"}) - out = stripRequiredPrefix(t, out, "hishtory tquery -pipefail") + out = captureTerminalOutput(t, tester, []string{"hishtory SPACE tquery ENTER", "-pipefail"}) + out = stripRequiredPrefix(t, out, "hishtory tquery") testutils.CompareGoldens(t, out, "testRemoveDuplicateRows-enabled-tquery") // Check actually selecting it with query out = captureTerminalOutputWithComplexCommands(t, tester, []TmuxCommand{ - {Keys: "hishtory SPACE tquery SPACE -pipefail ENTER", ExtraDelay: 1.0}, - {Keys: "Down Down"}, + {Keys: "hishtory SPACE tquery ENTER", ExtraDelay: 1.0}, + {Keys: "-pipefail", ExtraDelay: 1.0}, + {Keys: "Down Down Down"}, {Keys: "ENTER", ExtraDelay: 1.0}, }) out = stripTuiCommandPrefix(t, out) - require.Contains(t, out, "\necho foo\n") + require.Contains(t, out, "echo foo\n") + require.NotContains(t, out, "hishtory tquery") require.NotContains(t, out, "echo baz") require.NotContains(t, out, "config-set") } diff --git a/client/cmd/install.go b/client/cmd/install.go index 1123a26..e9c7cdc 100644 --- a/client/cmd/install.go +++ b/client/cmd/install.go @@ -26,10 +26,14 @@ import ( ) var ( - offlineInit *bool - forceInit *bool - offlineInstall *bool - skipConfigModification *bool + offlineInit *bool + forceInit *bool + offlineInstall *bool + skipConfigModification *bool + skipUpdateConfigModification *bool + + //lint:ignore U1000 Flag that is allowed to be specified, but not used + currentlyInstalledVersion *string ) var installCmd = &cobra.Command{ @@ -45,7 +49,7 @@ var installCmd = &cobra.Command{ if strings.HasPrefix(secretKey, "-") { lib.CheckFatalError(fmt.Errorf("secret key %#v looks like a CLI flag, please use a secret key that does not start with a -", secretKey)) } - lib.CheckFatalError(install(secretKey, *offlineInstall, *skipConfigModification)) + lib.CheckFatalError(install(secretKey, *offlineInstall, *skipConfigModification || *skipUpdateConfigModification)) if os.Getenv("HISHTORY_SKIP_INIT_IMPORT") == "" { db, err := hctx.OpenLocalSqliteDb() lib.CheckFatalError(err) @@ -686,4 +690,6 @@ func init() { forceInit = initCmd.Flags().Bool("force", false, "Force re-init without any prompts") offlineInstall = installCmd.Flags().Bool("offline", false, "Install hiSHtory in offline mode with all syncing capabilities disabled") skipConfigModification = installCmd.Flags().Bool("skip-config-modification", false, "Skip modifying shell configs and instead instruct the user on how to modify their configs") + skipUpdateConfigModification = installCmd.Flags().Bool("skip-update-config-modification", false, "Skip modifying shell configs for updates") + currentlyInstalledVersion = installCmd.Flags().String("currently-installed-version", "", "The currently installed version (used by the update command)") } diff --git a/client/cmd/query.go b/client/cmd/query.go index 4da2629..108fa12 100644 --- a/client/cmd/query.go +++ b/client/cmd/query.go @@ -55,7 +55,7 @@ var tqueryCmd = &cobra.Command{ if os.Getenv("HISHTORY_SHELL_NAME") != "" { shellName = os.Getenv("HISHTORY_SHELL_NAME") } - lib.CheckFatalError(tui.TuiQuery(ctx, shellName, strings.Join(args, " "))) + lib.CheckFatalError(tui.TuiQuery(ctx, shellName, args)) }, } diff --git a/client/lib/lib.go b/client/lib/lib.go index c59dd57..ec984f4 100644 --- a/client/lib/lib.go +++ b/client/lib/lib.go @@ -965,7 +965,9 @@ func splitEscaped(query string, separator rune, maxSplit int) []string { isInSingleQuotedString := false for i := 0; i < len(runeQuery); i++ { if (maxSplit < 0 || splits < maxSplit) && runeQuery[i] == separator && !isInSingleQuotedString && !isInDoubleQuotedString { - tokens = append(tokens, string(token)) + if string(token) != "" { + tokens = append(tokens, string(token)) + } token = token[:0] splits++ } else if runeQuery[i] == '\\' && i+1 < len(runeQuery) { @@ -982,8 +984,13 @@ func splitEscaped(query string, separator rune, maxSplit int) []string { } else if runeQuery[i] == '\'' && !isInDoubleQuotedString && !heuristicIgnoreUnclosedQuote(isInSingleQuotedString, '\'', runeQuery, i) { isInSingleQuotedString = !isInSingleQuotedString } else { - if (isInSingleQuotedString || isInDoubleQuotedString) && separator == ' ' && runeQuery[i] == ':' { - token = append(token, '\\') + if (isInSingleQuotedString || isInDoubleQuotedString) && separator == ' ' { + if runeQuery[i] == ':' { + token = append(token, '\\') + } + if runeQuery[i] == '-' && len(token) == 0 { + token = append(token, '\\') + } } token = append(token, runeQuery[i]) } diff --git a/client/lib/lib_test.go b/client/lib/lib_test.go index 2d3ad93..013b727 100644 --- a/client/lib/lib_test.go +++ b/client/lib/lib_test.go @@ -324,6 +324,11 @@ func TestSplitEscaped(t *testing.T) { {"foo:bar", ' ', -1, []string{"foo:bar"}}, {"'foo:bar'", ' ', -1, []string{"foo\\:bar"}}, {"\"foo:bar\"", ' ', -1, []string{"foo\\:bar"}}, + // Tests for quoting dashes + {"'-foo'", ' ', -1, []string{"\\-foo"}}, + {"'--foo'", ' ', -1, []string{"\\--foo"}}, + {"bar '--foo'", ' ', -1, []string{"bar", "\\--foo"}}, + {"bar 'foo-baz'", ' ', -1, []string{"bar", "foo-baz"}}, } for _, tc := range testcases { actual := splitEscaped(tc.input, tc.char, tc.limit) diff --git a/client/testdata/TestTimestampFormat-tquery b/client/testdata/TestTimestampFormat-tquery index 8b55daf..b45d9ba 100644 --- a/client/testdata/TestTimestampFormat-tquery +++ b/client/testdata/TestTimestampFormat-tquery @@ -1,10 +1,10 @@ -Search Query: > -pipefail -tablesizing +Search Query: > table_cmd -tquery ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ Hostname CWD Timestamp Runtime Exit Code Command │ +│ Hostname CWD Timestamp Runtime Exit Code Command │ │──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│ -│ localhost ~/foo/ 2022/Apr/16 01:03 24s 3 table_cmd2 │ -│ localhost /tmp/ 2022/Apr/16 01:03 4s 2 table_cmd1 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa… │ +│ localhost ~/foo/ 2022/Apr/16 01:03 24s 3 table_cmd2 │ +│ localhost /tmp/ 2022/Apr/16 01:03 4s 2 table_cmd1 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa… │ │ │ │ │ │ │ diff --git a/client/testdata/TestTui-SearchQuoteDash b/client/testdata/TestTui-SearchQuoteDash new file mode 100644 index 0000000..812b0b9 --- /dev/null +++ b/client/testdata/TestTui-SearchQuoteDash @@ -0,0 +1,27 @@ +Search Query: > "--bar" + +┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname CWD Timestamp Runtime Exit Code Command │ +│────────────────────────────────────────────────────────────────────────────────────────────────────────│ +│ localhost /tmp/ Oct 17 2022 21:43:46 PDT 3s 2 foo --bar │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +│ │ +└────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +hiSHtory: Search your shell history • ctrl+h help \ No newline at end of file diff --git a/client/testdata/testCustomColumns-tquery-bash-isAction b/client/testdata/testCustomColumns-tquery-bash-isAction index dd108e8..c913ad2 100644 --- a/client/testdata/testCustomColumns-tquery-bash-isAction +++ b/client/testdata/testCustomColumns-tquery-bash-isAction @@ -3,6 +3,7 @@ Search Query: > -pipefail ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Exit Code git_remote Command │ │────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│ +│ 0 https://github.com/ddworken/hishtory hishtory tquery │ │ 0 https://github.com/ddworken/hishtory hishtory config-set displayed-columns 'Exit Code' git_remote Command │ │ 0 echo bar │ │ 0 cd / │ @@ -22,6 +23,5 @@ Search Query: > -pipefail │ │ │ │ │ │ -│ │ └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ hiSHtory: Search your shell history • ctrl+h help \ No newline at end of file diff --git a/client/testdata/testCustomColumns-tquery-zsh-isAction b/client/testdata/testCustomColumns-tquery-zsh-isAction index dd108e8..c913ad2 100644 --- a/client/testdata/testCustomColumns-tquery-zsh-isAction +++ b/client/testdata/testCustomColumns-tquery-zsh-isAction @@ -3,6 +3,7 @@ Search Query: > -pipefail ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Exit Code git_remote Command │ │────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│ +│ 0 https://github.com/ddworken/hishtory hishtory tquery │ │ 0 https://github.com/ddworken/hishtory hishtory config-set displayed-columns 'Exit Code' git_remote Command │ │ 0 echo bar │ │ 0 cd / │ @@ -22,6 +23,5 @@ Search Query: > -pipefail │ │ │ │ │ │ -│ │ └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ hiSHtory: Search your shell history • ctrl+h help \ No newline at end of file diff --git a/client/testdata/testRemoveDuplicateRows-enabled-export b/client/testdata/testRemoveDuplicateRows-enabled-export index 05b21f2..d2da25c 100644 --- a/client/testdata/testRemoveDuplicateRows-enabled-export +++ b/client/testdata/testRemoveDuplicateRows-enabled-export @@ -4,4 +4,5 @@ echo baz echo baz echo foo hishtory config-set displayed-columns 'Exit Code' Command +hishtory tquery hishtory config-set filter-duplicate-commands true diff --git a/client/testdata/testRemoveDuplicateRows-enabled-query b/client/testdata/testRemoveDuplicateRows-enabled-query index d517fce..dc86486 100644 --- a/client/testdata/testRemoveDuplicateRows-enabled-query +++ b/client/testdata/testRemoveDuplicateRows-enabled-query @@ -1,5 +1,6 @@ Exit Code Command 0 hishtory config-set filter-duplicate-commands true +0 hishtory tquery 0 hishtory config-set displayed-columns 'Exit Code' Command 0 echo foo 0 echo baz diff --git a/client/testdata/testRemoveDuplicateRows-enabled-tquery b/client/testdata/testRemoveDuplicateRows-enabled-tquery index 75c9d99..eaae0e8 100644 --- a/client/testdata/testRemoveDuplicateRows-enabled-tquery +++ b/client/testdata/testRemoveDuplicateRows-enabled-tquery @@ -3,6 +3,7 @@ Search Query: > -pipefail ┌───────────────────────────────────────────────────────────────────────────┐ │ Exit Code Command │ │───────────────────────────────────────────────────────────────────────────│ +│ 0 hishtory tquery │ │ 0 hishtory config-set filter-duplicate-commands true │ │ 0 hishtory config-set displayed-columns 'Exit Code' Command │ │ 0 echo foo │ @@ -22,6 +23,5 @@ Search Query: > -pipefail │ │ │ │ │ │ -│ │ └───────────────────────────────────────────────────────────────────────────┘ hiSHtory: Search your shell history • ctrl+h help \ No newline at end of file diff --git a/client/testdata/testRemoveDuplicateRows-tquery b/client/testdata/testRemoveDuplicateRows-tquery index a33fe72..e5966e9 100644 --- a/client/testdata/testRemoveDuplicateRows-tquery +++ b/client/testdata/testRemoveDuplicateRows-tquery @@ -3,6 +3,7 @@ Search Query: > -pipefail ┌───────────────────────────────────────────────────────────────────────────┐ │ Exit Code Command │ │───────────────────────────────────────────────────────────────────────────│ +│ 0 hishtory tquery │ │ 0 hishtory config-set displayed-columns 'Exit Code' Command │ │ 0 echo foo │ │ 0 echo baz │ @@ -22,6 +23,5 @@ Search Query: > -pipefail │ │ │ │ │ │ -│ │ └───────────────────────────────────────────────────────────────────────────┘ hiSHtory: Search your shell history • ctrl+h help \ No newline at end of file diff --git a/client/testutils.go b/client/testutils.go index 39bbf49..caee5c3 100644 --- a/client/testutils.go +++ b/client/testutils.go @@ -361,7 +361,7 @@ func stripShellPrefix(out string) string { func stripRequiredPrefix(t *testing.T, out, prefix string) string { require.Contains(t, out, prefix) - return strings.TrimSpace(strings.Split(out, prefix)[1]) + return strings.TrimSpace(strings.SplitN(out, prefix, 2)[1]) } func stripTuiCommandPrefix(t *testing.T, out string) string { diff --git a/client/tui/tui.go b/client/tui/tui.go index 9a755e5..19f3bab 100644 --- a/client/tui/tui.go +++ b/client/tui/tui.go @@ -3,6 +3,7 @@ package tui import ( "context" _ "embed" // for embedding config.sh + "encoding/json" "fmt" "os" "path/filepath" @@ -95,6 +96,9 @@ type model struct { // The currently executing shell. Defaults to bash if not specified. Used for more precise AI suggestions. shellName string + + // Whether we've finished the first load of results. If we haven't, we refuse to run additional queries to avoid race conditions with how we handle invalid initial queries. + hasFinishedFirstLoad bool } type ( @@ -119,6 +123,8 @@ type asyncQueryFinishedMsg struct { maintainCursor bool // An updated search query. May be used for initial queries when they're invalid. overriddenSearchQuery *string + + isFirstQuery bool } func initialModel(ctx context.Context, shellName, initialQuery string) model { @@ -148,7 +154,7 @@ func initialModel(ctx context.Context, shellName, initialQuery string) model { queryInput.SetValue(initialQuery) } CURRENT_QUERY_FOR_HIGHLIGHTING = initialQuery - return model{ctx: ctx, spinner: s, isLoading: true, table: nil, tableEntries: []*data.HistoryEntry{}, runQuery: &initialQuery, queryInput: queryInput, help: help.New(), shellName: shellName} + return model{ctx: ctx, spinner: s, isLoading: true, table: nil, tableEntries: []*data.HistoryEntry{}, runQuery: &initialQuery, queryInput: queryInput, help: help.New(), shellName: shellName, hasFinishedFirstLoad: false} } func (m model) Init() tea.Cmd { @@ -199,13 +205,14 @@ func preventTableOverscrolling(m model) { func runQueryAndUpdateTable(m model, forceUpdateTable, maintainCursor bool) tea.Cmd { if (m.runQuery != nil && *m.runQuery != m.lastQuery) || forceUpdateTable || m.searchErr != nil { + // if !m.hasFinishedFirstLoad { + // return nil + // } query := m.lastQuery if m.runQuery != nil { query = *m.runQuery } - LAST_DISPATCHED_QUERY_ID++ - queryId := LAST_DISPATCHED_QUERY_ID - LAST_DISPATCHED_QUERY_TIMESTAMP = time.Now() + queryId := allocateQueryId() return func() tea.Msg { conf := hctx.GetConf(m.ctx) defaultFilter := conf.DefaultFilter @@ -214,7 +221,7 @@ func runQueryAndUpdateTable(m model, forceUpdateTable, maintainCursor bool) tea. defaultFilter = "" } rows, entries, searchErr := getRows(m.ctx, conf.DisplayedColumns, m.shellName, defaultFilter, query, getNumEntriesNeeded(m.ctx)) - return asyncQueryFinishedMsg{queryId, rows, entries, searchErr, forceUpdateTable, maintainCursor, nil} + return asyncQueryFinishedMsg{queryId, rows, entries, searchErr, forceUpdateTable, maintainCursor, nil, false} } } return nil @@ -331,6 +338,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.queryInput.SetValue(*msg.overriddenSearchQuery) } } + if msg.isFirstQuery { + m.hasFinishedFirstLoad = true + } return m, nil default: var cmd tea.Cmd @@ -879,28 +889,72 @@ func configureColorProfile(ctx context.Context) { } } -func TuiQuery(ctx context.Context, shellName, initialQuery string) error { +func buildInitialQueryWithSearchEscaping(initialQueryArray []string) (string, error) { + var initialQuery string + + for i, queryChunk := range initialQueryArray { + if i != 0 { + initialQuery += " " + } + if strings.HasPrefix(queryChunk, "-") { + quoted, err := json.Marshal(queryChunk) + if err != nil { + return "", fmt.Errorf("failed to marshal query chunk for escaping: %w", err) + } + initialQuery += string(quoted) + } else { + initialQuery += queryChunk + } + } + + return initialQuery, nil +} + +func splitQueryArray(initialQueryArray []string) []string { + var splitQueryArray []string + for _, queryChunk := range initialQueryArray { + splitQueryArray = append(splitQueryArray, strings.Split(queryChunk, " ")...) + } + return splitQueryArray +} + +func allocateQueryId() int { + LAST_DISPATCHED_QUERY_ID++ + LAST_DISPATCHED_QUERY_TIMESTAMP = time.Now() + return LAST_DISPATCHED_QUERY_ID +} + +func TuiQuery(ctx context.Context, shellName string, initialQueryArray []string) error { + initialQueryArray = splitQueryArray(initialQueryArray) + initialQueryWithEscaping, err := buildInitialQueryWithSearchEscaping(initialQueryArray) + if err != nil { + return err + } loadedKeyBindings = hctx.GetConf(ctx).KeyBindings.ToKeyMap() configureColorProfile(ctx) additionalOptions := []tea.ProgramOption{tea.WithOutput(os.Stderr)} if hctx.GetConf(ctx).FullScreenRendering { additionalOptions = append(additionalOptions, tea.WithAltScreen()) } - p := tea.NewProgram(initialModel(ctx, shellName, initialQuery), additionalOptions...) + p := tea.NewProgram(initialModel(ctx, shellName, initialQueryWithEscaping), additionalOptions...) // Async: Get the initial set of rows go func() { - LAST_DISPATCHED_QUERY_ID++ - queryId := LAST_DISPATCHED_QUERY_ID - LAST_DISPATCHED_QUERY_TIMESTAMP = time.Now() + queryId := allocateQueryId() conf := hctx.GetConf(ctx) - rows, entries, err := getRows(ctx, conf.DisplayedColumns, shellName, conf.DefaultFilter, initialQuery, getNumEntriesNeeded(ctx)) - if err == nil || initialQuery == "" { - p.Send(asyncQueryFinishedMsg{queryId: queryId, rows: rows, entries: entries, searchErr: err, forceUpdateTable: true, maintainCursor: false, overriddenSearchQuery: nil}) + rows, entries, err := getRows(ctx, conf.DisplayedColumns, shellName, conf.DefaultFilter, initialQueryWithEscaping, getNumEntriesNeeded(ctx)) + if err == nil || initialQueryWithEscaping == "" { + if err != nil { + panic(err) + } + p.Send(asyncQueryFinishedMsg{queryId: queryId, rows: rows, entries: entries, searchErr: err, forceUpdateTable: true, maintainCursor: false, overriddenSearchQuery: nil, isFirstQuery: true}) } else { - // initialQuery is likely invalid in some way, let's just drop it + // The initial query is likely invalid in some way, let's just drop it emptyQuery := "" rows, entries, err := getRows(ctx, hctx.GetConf(ctx).DisplayedColumns, shellName, conf.DefaultFilter, emptyQuery, getNumEntriesNeeded(ctx)) - p.Send(asyncQueryFinishedMsg{queryId: queryId, rows: rows, entries: entries, searchErr: err, forceUpdateTable: true, maintainCursor: false, overriddenSearchQuery: &emptyQuery}) + if err != nil { + panic(err) + } + p.Send(asyncQueryFinishedMsg{queryId: allocateQueryId(), rows: rows, entries: entries, searchErr: err, forceUpdateTable: true, maintainCursor: false, overriddenSearchQuery: &emptyQuery, isFirstQuery: true}) } }() // Async: Retrieve additional entries from the backend @@ -931,13 +985,13 @@ func TuiQuery(ctx context.Context, shellName, initialQuery string) error { p.Send(bannerMsg{banner: string(banner)}) }() // Blocking: Start the TUI - _, err := p.Run() + _, err = p.Run() if err != nil { return err } if SELECTED_COMMAND == "" && os.Getenv("HISHTORY_TERM_INTEGRATION") != "" { - // Print out the initialQuery instead so that we don't clear the terminal - SELECTED_COMMAND = initialQuery + // Print out the initialQuery instead so that we don't clear the terminal (note that we don't use the escaped one here) + SELECTED_COMMAND = strings.Join(initialQueryArray, " ") } fmt.Printf("%s\n", SELECTED_COMMAND) return nil @@ -945,4 +999,3 @@ func TuiQuery(ctx context.Context, shellName, initialQuery string) error { // TODO: support custom key bindings // TODO: make the help page wrap -// TODO: If the initial query contains dashes, maybe we should smartly escape them?