fix(cd): harden zoxide directory suggestions
This commit is contained in:
+22
-6
@@ -2,6 +2,7 @@ package spec
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -274,15 +275,15 @@ func Lookup(input string) []Suggestion {
|
||||
}
|
||||
|
||||
suggested := g.Cmd
|
||||
if strings.Contains(suggested, " ") && !strings.HasPrefix(suggested, "\"") {
|
||||
suggested = "\"" + suggested + "\""
|
||||
if filepath.IsAbs(g.Cmd) || strings.ContainsAny(g.Cmd, " \t\r\n") {
|
||||
suggested = quoteSuggestionArgument(g.Cmd)
|
||||
}
|
||||
|
||||
// if the suggestion is a full path that includes
|
||||
// words already in the command line (multi-word support), we replace
|
||||
// the entire argument part by using prefix only
|
||||
// Absolute results replace all typed arguments, avoiding duplication
|
||||
// for multi-word directory matches.
|
||||
finalCmd := ""
|
||||
if len(tokens) > depth+1 && strings.HasPrefix(g.Cmd, tokens[depth]) {
|
||||
replaceArguments := strings.HasPrefix(g.Cmd, tokens[depth]) || filepath.IsAbs(g.Cmd)
|
||||
if len(tokens) > depth+1 && replaceArguments {
|
||||
finalCmd = prefix + " " + suggested
|
||||
} else if genLinePrefix != "" {
|
||||
finalCmd = genLinePrefix + " " + suggested
|
||||
@@ -382,6 +383,21 @@ func Lookup(input string) []Suggestion {
|
||||
return results
|
||||
}
|
||||
|
||||
func quoteSuggestionArgument(argument string) string {
|
||||
prefix := ""
|
||||
value := argument
|
||||
if strings.HasPrefix(value, "~/") {
|
||||
prefix = "~/"
|
||||
value = strings.TrimPrefix(value, prefix)
|
||||
}
|
||||
if !strings.ContainsAny(value, " \t\r\n'\"\\$`!;&|<>()*?[]{}#^") {
|
||||
return argument
|
||||
}
|
||||
|
||||
escaped := strings.ReplaceAll(value, "'", `'\''`)
|
||||
return prefix + "'" + escaped + "'"
|
||||
}
|
||||
|
||||
func topLevelSuggestions(query string, aliases map[string]string) []Suggestion {
|
||||
scanExternalCommands()
|
||||
results, seen := []Suggestion{}, make(map[string]bool)
|
||||
|
||||
+17
-1
@@ -173,6 +173,23 @@ func TestLookup_NvimFileGenerator(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuoteSuggestionArgument(t *testing.T) {
|
||||
tests := map[string]string{
|
||||
"/tmp/project": "/tmp/project",
|
||||
"/tmp/Проект": "/tmp/Проект",
|
||||
"/tmp/My Project": `'/tmp/My Project'`,
|
||||
"/tmp/$Project`command`": "'/tmp/$Project`command`'",
|
||||
`/tmp/quote"and'apostrophe`: "'/tmp/quote\"and'\\''apostrophe'",
|
||||
"~/My Project": `~/'My Project'`,
|
||||
}
|
||||
|
||||
for input, want := range tests {
|
||||
if got := quoteSuggestionArgument(input); got != want {
|
||||
t.Errorf("quoteSuggestionArgument(%q) = %q, want %q", input, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookup_OptionAndFilePriority(t *testing.T) {
|
||||
ResetRegistry()
|
||||
Register(&Spec{
|
||||
@@ -389,4 +406,3 @@ func TestLookup_RealGitProvider(t *testing.T) {
|
||||
t.Errorf("expected 'git recent' in results for real GitProvider, got %v", results)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user