mirror of
https://github.com/ddworken/hishtory.git
synced 2025-08-13 16:57:23 +02:00
Quote initial commands to make it possible to easily use hishtory to find matching entries for already typed commands that contain flags
This commit is contained in:
@ -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))
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -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,13 +984,20 @@ 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])
|
||||
}
|
||||
}
|
||||
tokens = append(tokens, string(token))
|
||||
if string(token) != "" {
|
||||
tokens = append(tokens, string(token))
|
||||
}
|
||||
return tokens
|
||||
}
|
||||
|
||||
|
@ -306,8 +306,8 @@ func TestSplitEscaped(t *testing.T) {
|
||||
{"'foo\"bar", ' ', -1, []string{"'foo\"bar"}},
|
||||
{"\"foo'\\\"bar\"", ' ', -1, []string{"foo'\"bar"}},
|
||||
{"'foo\"\\'bar'", ' ', -1, []string{"foo\"'bar"}},
|
||||
{"''", ' ', -1, []string{""}},
|
||||
{"\"\"", ' ', -1, []string{""}},
|
||||
{"''", ' ', -1, nil},
|
||||
{"\"\"", ' ', -1, nil},
|
||||
{"\\\"", ' ', -1, []string{"\""}},
|
||||
{"\\'", ' ', -1, []string{"'"}},
|
||||
// Tests the behavior of quotes with
|
||||
@ -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)
|
||||
|
@ -3,6 +3,7 @@ package tui
|
||||
import (
|
||||
"context"
|
||||
_ "embed" // for embedding config.sh
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -865,21 +866,55 @@ 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 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)
|
||||
p := tea.NewProgram(initialModel(ctx, shellName, initialQuery), tea.WithOutput(os.Stderr))
|
||||
p := tea.NewProgram(initialModel(ctx, shellName, initialQueryWithEscaping), tea.WithOutput(os.Stderr))
|
||||
// Async: Get the initial set of rows
|
||||
go func() {
|
||||
LAST_DISPATCHED_QUERY_ID++
|
||||
queryId := LAST_DISPATCHED_QUERY_ID
|
||||
LAST_DISPATCHED_QUERY_TIMESTAMP = time.Now()
|
||||
conf := hctx.GetConf(ctx)
|
||||
rows, entries, err := getRows(ctx, conf.DisplayedColumns, shellName, conf.DefaultFilter, initialQuery, PADDED_NUM_ENTRIES)
|
||||
if err == nil || initialQuery == "" {
|
||||
rows, entries, err := getRows(ctx, conf.DisplayedColumns, shellName, conf.DefaultFilter, initialQueryWithEscaping, PADDED_NUM_ENTRIES)
|
||||
if err == nil || initialQueryWithEscaping == "" {
|
||||
p.Send(asyncQueryFinishedMsg{queryId: queryId, rows: rows, entries: entries, searchErr: err, forceUpdateTable: true, maintainCursor: false, overriddenSearchQuery: nil})
|
||||
} 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, PADDED_NUM_ENTRIES)
|
||||
p.Send(asyncQueryFinishedMsg{queryId: queryId, rows: rows, entries: entries, searchErr: err, forceUpdateTable: true, maintainCursor: false, overriddenSearchQuery: &emptyQuery})
|
||||
@ -913,13 +948,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
|
||||
|
Reference in New Issue
Block a user