feat(spec): system PATH scannee
This commit is contained in:
+62
-6
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user