feat(spec): better file path suggestions (#41)
Enhance file/folder suggestion
This commit is contained in:
+13
-6
@@ -104,8 +104,9 @@ func FileGenerator(filters ...string) GeneratorFunc {
|
||||
if entry.IsDir() {
|
||||
if dirOnly || len(filterSet) == 0 {
|
||||
results = append(results, Suggestion{
|
||||
Cmd: fullPath + "/",
|
||||
Desc: "directory",
|
||||
Cmd: fullPath + "/",
|
||||
Desc: "directory",
|
||||
Priority: 50,
|
||||
})
|
||||
} else {
|
||||
// scan only 1 level deeper if there is a filter
|
||||
@@ -122,8 +123,9 @@ func FileGenerator(filters ...string) GeneratorFunc {
|
||||
ext := strings.ToLower(filepath.Ext(subName))
|
||||
if filterSet[ext] {
|
||||
results = append(results, Suggestion{
|
||||
Cmd: fullPath + "/" + subName,
|
||||
Desc: "file",
|
||||
Cmd: fullPath + "/" + subName,
|
||||
Desc: "file",
|
||||
Priority: 50,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -143,9 +145,14 @@ func FileGenerator(filters ...string) GeneratorFunc {
|
||||
continue
|
||||
}
|
||||
}
|
||||
desc := "file"
|
||||
if ext := strings.ToLower(filepath.Ext(name)); ext != "" {
|
||||
desc = strings.TrimPrefix(ext, ".")
|
||||
}
|
||||
results = append(results, Suggestion{
|
||||
Cmd: fullPath,
|
||||
Desc: "file",
|
||||
Cmd: fullPath,
|
||||
Desc: desc,
|
||||
Priority: 50,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+39
-7
@@ -61,9 +61,13 @@ func Lookup(input string) []Suggestion {
|
||||
|
||||
scanExternalCommands()
|
||||
|
||||
// if you have an alias in your shell config like: alias gca="git commit -a"
|
||||
// if the first word match it, IRIS will suggest "git commit -a"
|
||||
if len(tokens) > 1 {
|
||||
// if you have an alias in your shell config like: alias nv="nvim"
|
||||
// expand it even when there's only one token (e.g. "nv ") so that the
|
||||
// target spec's Generator (FileGenerator etc.) can still fire.
|
||||
// only expand when there's a trailing space — i.e. the user has committed
|
||||
// to the alias name and is now typing arguments (tokens last elem == "")
|
||||
hasTrailingSpace := len(tokens) > 0 && tokens[len(tokens)-1] == ""
|
||||
if hasTrailingSpace || len(tokens) > 1 {
|
||||
if target, ok := aliases[tokens[0]]; ok {
|
||||
aliasTokens := Tokenize(target)
|
||||
if len(aliasTokens) > 0 && aliasTokens[len(aliasTokens)-1] == "" {
|
||||
@@ -202,10 +206,28 @@ func Lookup(input string) []Suggestion {
|
||||
linePrefix := linePrefixBuilder.String()
|
||||
|
||||
if currentGen != nil && allowMoreArgs {
|
||||
genResults := currentGen(tokens, prefix, partial)
|
||||
genPartial := partial
|
||||
genLinePrefix := linePrefix
|
||||
|
||||
if partial == "" && len(tokens) >= 3 {
|
||||
prevToken := tokens[len(tokens)-2]
|
||||
if strings.HasSuffix(prevToken, "/") || strings.HasSuffix(prevToken, "\\") {
|
||||
genPartial = prevToken
|
||||
lpBuilder := strings.Builder{}
|
||||
for i := 0; i < len(tokens)-2; i++ {
|
||||
if i > 0 {
|
||||
lpBuilder.WriteByte(' ')
|
||||
}
|
||||
lpBuilder.WriteString(tokens[i])
|
||||
}
|
||||
genLinePrefix = lpBuilder.String()
|
||||
}
|
||||
}
|
||||
|
||||
genResults := currentGen(tokens, prefix, genPartial)
|
||||
|
||||
for _, g := range genResults {
|
||||
if partial != "" && !HasPrefix(g.Cmd, partial) && !strings.Contains(g.Cmd, partial) {
|
||||
if genPartial != "" && !HasPrefix(g.Cmd, genPartial) && !strings.Contains(g.Cmd, genPartial) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -220,8 +242,10 @@ func Lookup(input string) []Suggestion {
|
||||
finalCmd := ""
|
||||
if len(tokens) > depth+1 && strings.HasPrefix(g.Cmd, tokens[depth]) {
|
||||
finalCmd = prefix + " " + suggested
|
||||
} else if genLinePrefix != "" {
|
||||
finalCmd = genLinePrefix + " " + suggested
|
||||
} else {
|
||||
finalCmd = strings.TrimSpace(linePrefix) + " " + suggested
|
||||
finalCmd = suggested
|
||||
}
|
||||
|
||||
newTokens := Tokenize(finalCmd)
|
||||
@@ -267,8 +291,16 @@ func Lookup(input string) []Suggestion {
|
||||
for _, opt := range currentOpts {
|
||||
trimmedOpt := strings.TrimLeft(opt.Name, "-")
|
||||
if !usedOpts[opt.Name] && (partial == "" || HasPrefix(opt.Name, partial) || HasPrefix(trimmedOpt, partial)) {
|
||||
optPriority := opt.Priority
|
||||
if optPriority == 0 {
|
||||
if strings.HasPrefix(partial, "-") {
|
||||
optPriority = 80
|
||||
} else {
|
||||
optPriority = 10
|
||||
}
|
||||
}
|
||||
results = append(results, Suggestion{
|
||||
Cmd: linePrefix + " " + opt.Name, Desc: opt.Description, Icon: rootCmdName, Priority: opt.Priority,
|
||||
Cmd: linePrefix + " " + opt.Name, Desc: opt.Description, Icon: rootCmdName, Priority: optPriority,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package spec
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -136,3 +138,96 @@ func TestLookupConcurrent(t *testing.T) {
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestLookup_AliasFileGenerator(t *testing.T) {
|
||||
ResetRegistry()
|
||||
Register(&Spec{
|
||||
Name: "nvim",
|
||||
Generator: FileGenerator(),
|
||||
})
|
||||
// alias "nv" -> "nvim", single token with trailing space
|
||||
ShellAliases = map[string]string{"nv": "nvim"}
|
||||
|
||||
results := Lookup("nv ")
|
||||
if len(results) == 0 {
|
||||
t.Errorf("expected file suggestions for alias 'nv ' -> 'nvim', got none")
|
||||
}
|
||||
for _, r := range results {
|
||||
if !strings.HasPrefix(r.Cmd, "nvim ") {
|
||||
t.Errorf("expected suggestion to start with 'nvim ', got %q", r.Cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookup_NvimFileGenerator(t *testing.T) {
|
||||
ResetRegistry()
|
||||
Register(&Spec{
|
||||
Name: "nvim",
|
||||
Generator: FileGenerator(),
|
||||
})
|
||||
results := Lookup("nvim ")
|
||||
if len(results) == 0 {
|
||||
t.Errorf("expected file suggestions for 'nvim ', got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookup_OptionAndFilePriority(t *testing.T) {
|
||||
ResetRegistry()
|
||||
Register(&Spec{
|
||||
Name: "nvim",
|
||||
Generator: FileGenerator(),
|
||||
Options: []Option{
|
||||
{Name: "-c", Description: "Execute cmd"},
|
||||
{Name: "--cmd", Description: "Execute cmd before config"},
|
||||
},
|
||||
})
|
||||
|
||||
// When query is 'nvim ', files should be prioritized over flags
|
||||
resultsEmpty := Lookup("nvim ")
|
||||
if len(resultsEmpty) == 0 {
|
||||
t.Fatalf("expected results for 'nvim ', got 0")
|
||||
}
|
||||
// first result should be a file or dir (Priority 50), not option (Priority 10)
|
||||
if strings.HasPrefix(resultsEmpty[0].Cmd, "nvim -") {
|
||||
t.Errorf("expected file/dir as top result for 'nvim ', got %q", resultsEmpty[0].Cmd)
|
||||
}
|
||||
|
||||
// When query is 'nvim -', flags should be prioritized (Priority 80)
|
||||
resultsDash := Lookup("nvim -")
|
||||
if len(resultsDash) == 0 {
|
||||
t.Fatalf("expected results for 'nvim -', got 0")
|
||||
}
|
||||
if !strings.HasPrefix(resultsDash[0].Cmd, "nvim -") {
|
||||
t.Errorf("expected option as top result for 'nvim -', got %q", resultsDash[0].Cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookup_NestedDirectoryTrailingSpace(t *testing.T) {
|
||||
ResetRegistry()
|
||||
Register(&Spec{
|
||||
Name: "cat",
|
||||
Generator: FileGenerator(),
|
||||
})
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
subDir := filepath.Join(tmpDir, "subdir")
|
||||
_ = os.Mkdir(subDir, 0755)
|
||||
testFile := filepath.Join(subDir, "hello.txt")
|
||||
_ = os.WriteFile(testFile, []byte("hi"), 0644)
|
||||
|
||||
query := "cat " + subDir + "/ "
|
||||
results := Lookup(query)
|
||||
if len(results) == 0 {
|
||||
t.Fatalf("expected results for %q, got 0", query)
|
||||
}
|
||||
found := false
|
||||
for _, r := range results {
|
||||
if strings.Contains(r.Cmd, "hello.txt") {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("expected hello.txt in results, got %v", results)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user