Add global flags for command

Fixes #24
This commit is contained in:
Eyal Posener 2017-05-15 19:32:59 +03:00
parent e00c0546bc
commit 096b79324e
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.
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
// given without any flag before.
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.
func (c *Command) predict(a Args) (options []string, only bool) {
// if wordCompleted has something that needs to follow it,
// 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
// search sub commands for predictions first
subCommandFound := false
for i, arg := range a.Completed {
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 !subCommandFound {
options = append(options, c.Sub.Predict(a)...)
// if last completed word is a global flag that we need to complete
if predictor, ok := c.GlobalFlags[a.LastCompleted]; ok && predictor != nil {
Log("Predicting according to global flag %s", a.LastCompleted)
return predictor.Predict(a), true
}
// add global available complete options
options = append(options, c.Flags.Predict(a)...)
options = append(options, c.GlobalFlags.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 {
options = append(options, c.Args.Predict(a)...)
}

View File

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

View File

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

View File

@ -85,15 +85,20 @@ func main() {
// define flags of the 'run' main command
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
// it, the tab completion will auto complete for files matching
// the given pattern.
"-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.