Merge pull request #25 from posener/global

Add global flags for command
This commit is contained in:
Eyal Posener 2017-05-15 20:06:16 +03:00 committed by GitHub
commit 3db55cf7d9
4 changed files with 50 additions and 41 deletions

View File

@ -15,6 +15,10 @@ type Command struct {
// The key is the flag name, and the value is it's predictions. // The key is the flag name, and the value is it's predictions.
Flags Flags Flags Flags
// GlobalFlags is a map of flags that the command accepts.
// Global flags that can appear also after a sub command.
GlobalFlags Flags
// Args are extra arguments that the command accepts, those who are // Args are extra arguments that the command accepts, those who are
// given without any flag before. // given without any flag before.
Args Predictor Args Predictor
@ -58,14 +62,7 @@ func (f Flags) Predict(a Args) (prediction []string) {
// and other flags or sub commands can't come after it. // and other flags or sub commands can't come after it.
func (c *Command) predict(a Args) (options []string, only bool) { func (c *Command) predict(a Args) (options []string, only bool) {
// if wordCompleted has something that needs to follow it, // search sub commands for predictions first
// it is the most relevant completion
if predictor, ok := c.Flags[a.LastCompleted]; ok && predictor != nil {
Log("Predicting according to flag %s", a.Last)
return predictor.Predict(a), true
}
// search sub commands for predictions
subCommandFound := false subCommandFound := false
for i, arg := range a.Completed { for i, arg := range a.Completed {
if cmd, ok := c.Sub[arg]; ok { if cmd, ok := c.Sub[arg]; ok {
@ -79,15 +76,28 @@ func (c *Command) predict(a Args) (options []string, only bool) {
} }
} }
// if no sub command was found, return a list of the sub commands // if last completed word is a global flag that we need to complete
if !subCommandFound { if predictor, ok := c.GlobalFlags[a.LastCompleted]; ok && predictor != nil {
options = append(options, c.Sub.Predict(a)...) Log("Predicting according to global flag %s", a.LastCompleted)
return predictor.Predict(a), true
} }
// add global available complete options options = append(options, c.GlobalFlags.Predict(a)...)
options = append(options, c.Flags.Predict(a)...)
// add additional expected argument of the command // if a sub command was entered, we won't add the parent command
// completions and we return here.
if subCommandFound {
return
}
// if last completed word is a command flag that we need to complete
if predictor, ok := c.Flags[a.LastCompleted]; ok && predictor != nil {
Log("Predicting according to flag %s", a.LastCompleted)
return predictor.Predict(a), true
}
options = append(options, c.Sub.Predict(a)...)
options = append(options, c.Flags.Predict(a)...)
if c.Args != nil { if c.Args != nil {
options = append(options, c.Args.Predict(a)...) options = append(options, c.Args.Predict(a)...)
} }

View File

@ -11,36 +11,30 @@ func TestCompleter_Complete(t *testing.T) {
initTests() initTests()
c := Command{ c := Command{
Sub: map[string]Command{ Sub: Commands{
"sub1": { "sub1": {
Flags: map[string]Predictor{ Flags: Flags{
"-flag1": PredictAnything, "-flag1": PredictAnything,
"-flag2": PredictNothing, "-flag2": PredictNothing,
}, },
}, },
"sub2": { "sub2": {
Flags: map[string]Predictor{ Flags: Flags{
"-flag2": PredictNothing, "-flag2": PredictNothing,
"-flag3": PredictSet("opt1", "opt2", "opt12"), "-flag3": PredictSet("opt1", "opt2", "opt12"),
}, },
Args: PredictFiles("*.md"), Args: PredictFiles("*.md"),
}, },
}, },
Flags: map[string]Predictor{ Flags: Flags{
"-o": PredictFiles("*.txt"),
},
GlobalFlags: Flags{
"-h": PredictNothing, "-h": PredictNothing,
"-global1": PredictAnything, "-global1": PredictAnything,
"-o": PredictFiles("*.txt"),
}, },
} }
allGlobals := []string{}
for sub := range c.Sub {
allGlobals = append(allGlobals, sub)
}
for flag := range c.Flags {
allGlobals = append(allGlobals, flag)
}
testTXTFiles := []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt"} testTXTFiles := []string{"./a.txt", "./b.txt", "./c.txt", "./.dot.txt"}
tests := []struct { tests := []struct {
@ -49,7 +43,7 @@ func TestCompleter_Complete(t *testing.T) {
}{ }{
{ {
args: "", args: "",
want: allGlobals, want: []string{"sub1", "sub2", "-h", "-global1", "-o"},
}, },
{ {
args: "-", args: "-",
@ -57,7 +51,7 @@ func TestCompleter_Complete(t *testing.T) {
}, },
{ {
args: "-h ", args: "-h ",
want: allGlobals, want: []string{"sub1", "sub2", "-h", "-global1", "-o"},
}, },
{ {
args: "-global1 ", // global1 is known follow flag args: "-global1 ", // global1 is known follow flag
@ -77,11 +71,11 @@ func TestCompleter_Complete(t *testing.T) {
}, },
{ {
args: "sub1 ", args: "sub1 ",
want: []string{"-flag1", "-flag2", "-h", "-global1", "-o"}, want: []string{"-flag1", "-flag2", "-h", "-global1"},
}, },
{ {
args: "sub2 ", args: "sub2 ",
want: []string{"./", "./dir/", "./outer/", "./readme.md", "-flag2", "-flag3", "-h", "-global1", "-o"}, want: []string{"./", "./dir/", "./outer/", "./readme.md", "-flag2", "-flag3", "-h", "-global1"},
}, },
{ {
args: "sub2 ./", args: "sub2 ./",
@ -93,7 +87,7 @@ func TestCompleter_Complete(t *testing.T) {
}, },
{ {
args: "sub2 -flag2 ", args: "sub2 -flag2 ",
want: []string{"./", "./dir/", "./outer/", "./readme.md", "-flag2", "-flag3", "-h", "-global1", "-o"}, want: []string{"./", "./dir/", "./outer/", "./readme.md", "-flag2", "-flag3", "-h", "-global1"},
}, },
{ {
args: "sub1 -fl", args: "sub1 -fl",
@ -109,7 +103,7 @@ func TestCompleter_Complete(t *testing.T) {
}, },
{ {
args: "sub1 -flag2 ", args: "sub1 -flag2 ",
want: []string{"-flag1", "-flag2", "-h", "-global1", "-o"}, want: []string{"-flag1", "-flag2", "-h", "-global1"},
}, },
{ {
args: "-no-such-flag", args: "-no-such-flag",
@ -117,7 +111,7 @@ func TestCompleter_Complete(t *testing.T) {
}, },
{ {
args: "-no-such-flag ", args: "-no-such-flag ",
want: allGlobals, want: []string{"sub1", "sub2", "-h", "-global1", "-o"},
}, },
{ {
args: "no-such-command", args: "no-such-command",
@ -125,7 +119,7 @@ func TestCompleter_Complete(t *testing.T) {
}, },
{ {
args: "no-such-command ", args: "no-such-command ",
want: allGlobals, want: []string{"sub1", "sub2", "-h", "-global1", "-o"},
}, },
{ {
args: "-o ", args: "-o ",
@ -149,7 +143,7 @@ func TestCompleter_Complete(t *testing.T) {
}, },
{ {
args: "-o ./readme.md ", args: "-o ./readme.md ",
want: allGlobals, want: []string{"sub1", "sub2", "-h", "-global1", "-o"},
}, },
{ {
args: "-o sub2 -flag3 ", args: "-o sub2 -flag3 ",

View File

@ -181,7 +181,7 @@ func main() {
"fix": fix, "fix": fix,
"version": version, "version": version,
}, },
Flags: complete.Flags{ GlobalFlags: complete.Flags{
"-h": complete.PredictNothing, "-h": complete.PredictNothing,
}, },
} }

View File

@ -85,15 +85,20 @@ func main() {
// define flags of the 'run' main command // define flags of the 'run' main command
Flags: complete.Flags{ Flags: complete.Flags{
// a flag '-h' which does not expects anything after it
"-h": complete.PredictNothing,
// a flag -o, which expects a file ending with .out after // a flag -o, which expects a file ending with .out after
// it, the tab completion will auto complete for files matching // it, the tab completion will auto complete for files matching
// the given pattern. // the given pattern.
"-o": complete.PredictFiles("*.out"), "-o": complete.PredictFiles("*.out"),
}, },
// define gloabl flags of the 'run' main command
// those will show up also when a sub command was entered in the
// command line
Flags: complete.Flags{
// a flag '-h' which does not expects anything after it
"-h": complete.PredictNothing,
},
} }
// run the command completion, as part of the main() function. // run the command completion, as part of the main() function.