feat(spec): better file path suggestions (#41)

Enhance file/folder suggestion
This commit is contained in:
VERSE
2026-07-26 20:43:05 +07:00
committed by GitHub
parent c4e9bcdd88
commit 27e0792560
24 changed files with 183 additions and 16 deletions
+13 -6
View File
@@ -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
View File
@@ -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,
})
}
}
+95
View File
@@ -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)
}
}