feat(spec): system PATH scannee

This commit is contained in:
verse91
2026-04-11 20:49:58 +07:00
parent 870b77bda5
commit 525eac1bfa
+62 -6
View File
@@ -1,6 +1,11 @@
package core package core
import "strings" import (
"os"
"path/filepath"
"strings"
"sync"
)
type GeneratorFunc func(tokens []string, prefix string, partial string) []Suggestion type GeneratorFunc func(tokens []string, prefix string, partial string) []Suggestion
@@ -35,7 +40,35 @@ type Suggestion struct {
Icon string Icon string
} }
var registry = map[string]*Spec{} var (
registry = map[string]*Spec{}
pathCmds = make(map[string]bool)
pathOnce sync.Once
)
// scanPath populates pathCmds with all executable files found in $PATH
func scanPath() {
pathVar := os.Getenv("PATH")
dirs := filepath.SplitList(pathVar)
for _, dir := range dirs {
files, err := os.ReadDir(dir)
if err != nil {
continue
}
for _, f := range files {
if f.IsDir() {
continue
}
// check if it's executable
info, err := f.Info()
if err == nil && info.Mode()&0111 != 0 {
pathCmds[f.Name()] = true
}
}
}
}
// Register adds a spec to the global registry // Register adds a spec to the global registry
func Register(s *Spec) { func Register(s *Spec) {
@@ -58,7 +91,7 @@ func Lookup(input string) []Suggestion {
if len(tokens) == 0 { if len(tokens) == 0 {
return topLevelSuggestions(input) return topLevelSuggestions(input)
} }
// if only 1 token and no trailing space, user is still typing the root command // if only 1 token and no trailing space, user is still typing the root command
if len(tokens) == 1 { if len(tokens) == 1 {
return topLevelSuggestions(tokens[0]) return topLevelSuggestions(tokens[0])
@@ -104,7 +137,7 @@ func Lookup(input string) []Suggestion {
continue continue
} }
// if no subcommand matches but we have a generator, // if no subcommand matches but we have a generator,
// and this is NOT the last token (meaning it's a finished argument), // and this is NOT the last token (meaning it's a finished argument),
// we consume it and move depth forward. // we consume it and move depth forward.
if currentGen != nil && depth < len(tokens)-1 { if currentGen != nil && depth < len(tokens)-1 {
@@ -190,7 +223,11 @@ func Lookup(input string) []Suggestion {
} }
func topLevelSuggestions(query string) []Suggestion { func topLevelSuggestions(query string) []Suggestion {
pathOnce.Do(scanPath)
results := []Suggestion{} results := []Suggestion{}
seen := make(map[string]bool)
for name, spec := range registry { for name, spec := range registry {
if query == "" || hasPrefix(name, query) { if query == "" || hasPrefix(name, query) {
results = append(results, Suggestion{ results = append(results, Suggestion{
@@ -198,8 +235,23 @@ func topLevelSuggestions(query string) []Suggestion {
Desc: spec.Description, Desc: spec.Description,
Icon: name, Icon: name,
}) })
seen[name] = true
} }
} }
for name := range pathCmds {
if seen[name] {
continue
}
if query == "" || hasPrefix(name, query) {
results = append(results, Suggestion{
Cmd: name,
Desc: "system command",
Icon: "root",
})
}
}
return results return results
} }
@@ -224,8 +276,12 @@ func hasPrefix(s, prefix string) bool {
} }
for i := 0; i < len(prefix); i++ { for i := 0; i < len(prefix); i++ {
a, b := s[i], prefix[i] a, b := s[i], prefix[i]
if a >= 'A' && a <= 'Z' { a += 32 } if a >= 'A' && a <= 'Z' {
if b >= 'A' && b <= 'Z' { b += 32 } a += 32
}
if b >= 'A' && b <= 'Z' {
b += 32
}
if a != b { if a != b {
return false return false
} }