Use local completion files and directories according to typed command
This commit is contained in:
parent
60345dc6db
commit
dc4c327ae8
|
@ -17,11 +17,11 @@ func (c *Command) options(args []string) (options []Option, only bool) {
|
|||
|
||||
// remove the first argument, which is the command name
|
||||
args = args[1:]
|
||||
|
||||
last := last(args)
|
||||
// if prev has something that needs to follow it,
|
||||
// it is the most relevant completion
|
||||
if predicate, ok := c.Flags[last(args)]; ok && predicate != nil {
|
||||
return predicate.predict(), true
|
||||
if predicate, ok := c.Flags[last]; ok && predicate != nil {
|
||||
return predicate.predict(last), true
|
||||
}
|
||||
|
||||
sub, options, only := c.searchSub(args)
|
||||
|
@ -41,7 +41,7 @@ func (c *Command) options(args []string) (options []Option, only bool) {
|
|||
}
|
||||
|
||||
// add additional expected argument of the command
|
||||
options = append(options, c.Args.predict()...)
|
||||
options = append(options, c.Args.predict(last)...)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ var (
|
|||
predictEllipsis = complete.PredictSet("./...")
|
||||
|
||||
goFilesOrPackages = complete.PredictFiles("**.go").
|
||||
Or(complete.PredictDirs("./")).
|
||||
Or(complete.PredictDirs).
|
||||
Or(predictEllipsis)
|
||||
)
|
||||
|
||||
|
@ -34,7 +34,7 @@ func main() {
|
|||
"-installsuffix": complete.PredictAnything,
|
||||
"-ldflags": complete.PredictAnything,
|
||||
"-linkshared": complete.PredictNothing,
|
||||
"-pkgdir": complete.PredictDirs("./"),
|
||||
"-pkgdir": complete.PredictDirs,
|
||||
"-tags": complete.PredictAnything,
|
||||
"-toolexec": complete.PredictAnything,
|
||||
},
|
||||
|
@ -45,7 +45,7 @@ func main() {
|
|||
Flags: complete.Flags{
|
||||
"-exec": complete.PredictAnything,
|
||||
},
|
||||
Args: complete.PredictFiles("**.go"),
|
||||
Args: complete.PredictFiles("*.go"),
|
||||
}
|
||||
|
||||
test := complete.Command{
|
||||
|
@ -59,23 +59,23 @@ func main() {
|
|||
"-count": complete.PredictAnything,
|
||||
"-cover": complete.PredictNothing,
|
||||
"-covermode": complete.PredictSet("set", "count", "atomic"),
|
||||
"-coverpkg": complete.PredictDirs("./"),
|
||||
"-coverpkg": complete.PredictDirs,
|
||||
"-cpu": complete.PredictAnything,
|
||||
"-run": predictTest("test"),
|
||||
"-short": complete.PredictNothing,
|
||||
"-timeout": complete.PredictAnything,
|
||||
|
||||
"-benchmem": complete.PredictNothing,
|
||||
"-blockprofile": complete.PredictFiles("**.out"),
|
||||
"-blockprofile": complete.PredictFiles("*.out"),
|
||||
"-blockprofilerate": complete.PredictAnything,
|
||||
"-coverprofile": complete.PredictFiles("**.out"),
|
||||
"-cpuprofile": complete.PredictFiles("**.out"),
|
||||
"-memprofile": complete.PredictFiles("**.out"),
|
||||
"-coverprofile": complete.PredictFiles("*.out"),
|
||||
"-cpuprofile": complete.PredictFiles("*.out"),
|
||||
"-memprofile": complete.PredictFiles("*.out"),
|
||||
"-memprofilerate": complete.PredictAnything,
|
||||
"-mutexprofile": complete.PredictFiles("**.out"),
|
||||
"-mutexprofile": complete.PredictFiles("*.out"),
|
||||
"-mutexprofilefraction": complete.PredictAnything,
|
||||
"-outputdir": complete.PredictDirs("./"),
|
||||
"-trace": complete.PredictFiles("**.out"),
|
||||
"-outputdir": complete.PredictDirs,
|
||||
"-trace": complete.PredictFiles("*.out"),
|
||||
},
|
||||
Args: goFilesOrPackages,
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ func main() {
|
|||
"-n": complete.PredictNothing,
|
||||
"-x": complete.PredictNothing,
|
||||
},
|
||||
Args: complete.PredictDirs("./"),
|
||||
Args: complete.PredictDirs,
|
||||
}
|
||||
|
||||
list := complete.Command{
|
||||
|
@ -124,7 +124,7 @@ func main() {
|
|||
"-f": complete.PredictAnything,
|
||||
"-json": complete.PredictNothing,
|
||||
},
|
||||
Args: complete.PredictDirs("./"),
|
||||
Args: complete.PredictDirs,
|
||||
}
|
||||
|
||||
tool := complete.Command{
|
||||
|
@ -141,7 +141,7 @@ func main() {
|
|||
"-n": complete.PredictNothing,
|
||||
"-x": complete.PredictNothing,
|
||||
},
|
||||
Args: complete.PredictDirs("./"),
|
||||
Args: complete.PredictDirs,
|
||||
}
|
||||
|
||||
env := complete.Command{
|
||||
|
@ -152,7 +152,7 @@ func main() {
|
|||
version := complete.Command{}
|
||||
|
||||
fix := complete.Command{
|
||||
Args: complete.PredictDirs("./"),
|
||||
Args: complete.PredictDirs,
|
||||
}
|
||||
|
||||
// commands that also accepts the build flags
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
|
||||
func predictTest(testType string) *complete.Predicate {
|
||||
return &complete.Predicate{
|
||||
Predictor: func() []complete.Option {
|
||||
Predictor: func(last string) []complete.Option {
|
||||
tests := testNames(testType)
|
||||
options := make([]complete.Option, len(tests))
|
||||
for i := range tests {
|
||||
|
|
61
predicate.go
61
predicate.go
|
@ -9,7 +9,7 @@ import (
|
|||
type Predicate struct {
|
||||
// Predictor is function that returns list of arguments that can
|
||||
// come after the flag/command
|
||||
Predictor func() []Option
|
||||
Predictor func(last string) []Option
|
||||
}
|
||||
|
||||
// Or unions two predicate struct, so that the result predicate
|
||||
|
@ -19,25 +19,26 @@ func (p *Predicate) Or(other *Predicate) *Predicate {
|
|||
return nil
|
||||
}
|
||||
return &Predicate{
|
||||
Predictor: func() []Option { return append(p.predict(), other.predict()...) },
|
||||
Predictor: func(last string) []Option { return append(p.predict(last), other.predict(last)...) },
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Predicate) predict() []Option {
|
||||
func (p *Predicate) predict(last string) []Option {
|
||||
if p == nil || p.Predictor == nil {
|
||||
return nil
|
||||
}
|
||||
return p.Predictor()
|
||||
return p.Predictor(last)
|
||||
}
|
||||
|
||||
var (
|
||||
PredictNothing *Predicate = nil
|
||||
PredictAnything = &Predicate{}
|
||||
PredictDirs = &Predicate{Predictor: dirs}
|
||||
)
|
||||
|
||||
func PredictSet(options ...string) *Predicate {
|
||||
return &Predicate{
|
||||
Predictor: func() []Option {
|
||||
Predictor: func(last string) []Option {
|
||||
ret := make([]Option, len(options))
|
||||
for i := range options {
|
||||
ret[i] = Arg(options[i])
|
||||
|
@ -51,29 +52,29 @@ func PredictFiles(pattern string) *Predicate {
|
|||
return &Predicate{Predictor: glob(pattern)}
|
||||
}
|
||||
|
||||
func PredictDirs(path string) *Predicate {
|
||||
return &Predicate{Predictor: dirs(path)}
|
||||
func dirs(last string) (options []Option) {
|
||||
dir := dirFromLast(last)
|
||||
return dirsAt(dir)
|
||||
}
|
||||
|
||||
func dirs(path string) func() []Option {
|
||||
return func() (options []Option) {
|
||||
dirs := []string{}
|
||||
filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
dirs = append(dirs, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if !filepath.IsAbs(path) {
|
||||
filesToRel(dirs)
|
||||
func dirsAt(path string) []Option {
|
||||
dirs := []string{}
|
||||
filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
dirs = append(dirs, path)
|
||||
}
|
||||
return filesToOptions(dirs)
|
||||
return nil
|
||||
})
|
||||
if !filepath.IsAbs(path) {
|
||||
filesToRel(dirs)
|
||||
}
|
||||
return filesToOptions(dirs)
|
||||
}
|
||||
|
||||
func glob(pattern string) func() []Option {
|
||||
return func() []Option {
|
||||
files, err := filepath.Glob(pattern)
|
||||
func glob(pattern string) func(last string) []Option {
|
||||
return func(last string) []Option {
|
||||
dir := dirFromLast(last)
|
||||
files, err := filepath.Glob(filepath.Join(dir, pattern))
|
||||
if err != nil {
|
||||
Log("failed glob operation with pattern '%s': %s", pattern, err)
|
||||
}
|
||||
|
@ -83,6 +84,7 @@ func glob(pattern string) func() []Option {
|
|||
return filesToOptions(files)
|
||||
}
|
||||
}
|
||||
|
||||
func filesToRel(files []string) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
|
@ -97,6 +99,9 @@ func filesToRel(files []string) {
|
|||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if rel == "." {
|
||||
rel = ""
|
||||
}
|
||||
files[i] = "./" + rel
|
||||
}
|
||||
return
|
||||
|
@ -109,3 +114,15 @@ func filesToOptions(files []string) []Option {
|
|||
}
|
||||
return options
|
||||
}
|
||||
|
||||
// dirFromLast gives the directory of the current written
|
||||
// last argument if it represents a file name being written.
|
||||
// in case that it is not, we fall back to the current directory.
|
||||
func dirFromLast(last string) string {
|
||||
dir := filepath.Dir(last)
|
||||
_, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
return "./"
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
|
46
run_test.go
46
run_test.go
|
@ -12,6 +12,10 @@ func TestCompleter_Complete(t *testing.T) {
|
|||
if testing.Verbose() {
|
||||
os.Setenv(envDebug, "1")
|
||||
}
|
||||
err := os.Chdir("./tests")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
c := Command{
|
||||
Sub: map[string]Command{
|
||||
|
@ -26,13 +30,13 @@ func TestCompleter_Complete(t *testing.T) {
|
|||
"-flag2": PredictNothing,
|
||||
"-flag3": PredictSet("opt1", "opt2", "opt12"),
|
||||
},
|
||||
Args: PredictDirs("./tests/").Or(PredictFiles("./tests/*.md")),
|
||||
Args: PredictDirs.Or(PredictFiles("*.md")),
|
||||
},
|
||||
},
|
||||
Flags: map[string]*Predicate{
|
||||
"-h": PredictNothing,
|
||||
"-global1": PredictAnything,
|
||||
"-o": PredictFiles("./tests/*.txt"),
|
||||
"-o": PredictFiles("*.txt"),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -44,7 +48,7 @@ func TestCompleter_Complete(t *testing.T) {
|
|||
allGlobals = append(allGlobals, flag)
|
||||
}
|
||||
|
||||
testTXTFiles := []string{"./tests/a.txt", "./tests/b.txt", "./tests/c.txt"}
|
||||
testTXTFiles := []string{"./a.txt", "./b.txt", "./c.txt"}
|
||||
|
||||
tests := []struct {
|
||||
args string
|
||||
|
@ -84,19 +88,19 @@ func TestCompleter_Complete(t *testing.T) {
|
|||
},
|
||||
{
|
||||
args: "sub2 ",
|
||||
want: []string{"./tests", "-flag2", "-flag3", "-h", "-global1", "-o"},
|
||||
want: []string{"./", "./dir", "./readme.md", "-flag2", "-flag3", "-h", "-global1", "-o"},
|
||||
},
|
||||
{
|
||||
args: "sub2 tests",
|
||||
want: []string{"./tests", "./tests/readme.md", "./tests/dir"},
|
||||
args: "sub2 ./",
|
||||
want: []string{"./", "./readme.md", "./dir"},
|
||||
},
|
||||
{
|
||||
args: "sub2 tests/re",
|
||||
want: []string{"./tests/readme.md"},
|
||||
args: "sub2 re",
|
||||
want: []string{"./readme.md"},
|
||||
},
|
||||
{
|
||||
args: "sub2 -flag2 ",
|
||||
want: []string{"./tests", "-flag2", "-flag3", "-h", "-global1", "-o"},
|
||||
want: []string{"./", "./dir", "./readme.md", "-flag2", "-flag3", "-h", "-global1", "-o"},
|
||||
},
|
||||
{
|
||||
args: "sub1 -fl",
|
||||
|
@ -132,30 +136,30 @@ func TestCompleter_Complete(t *testing.T) {
|
|||
},
|
||||
{
|
||||
args: "-o ",
|
||||
want: []string{},
|
||||
},
|
||||
{
|
||||
args: "-o ./tes",
|
||||
want: []string{},
|
||||
},
|
||||
{
|
||||
args: "-o tests/",
|
||||
want: testTXTFiles,
|
||||
},
|
||||
{
|
||||
args: "-o tests",
|
||||
args: "-o ./no-su",
|
||||
want: []string{},
|
||||
},
|
||||
{
|
||||
args: "-o ./",
|
||||
want: testTXTFiles,
|
||||
},
|
||||
{
|
||||
args: "-o ./compl",
|
||||
args: "-o ",
|
||||
want: testTXTFiles,
|
||||
},
|
||||
{
|
||||
args: "-o ./read",
|
||||
want: []string{},
|
||||
},
|
||||
{
|
||||
args: "-o ./complete.go",
|
||||
args: "-o ./readme.md",
|
||||
want: []string{},
|
||||
},
|
||||
{
|
||||
args: "-o ./complete.go ",
|
||||
args: "-o ./readme.md ",
|
||||
want: allGlobals,
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue