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