From 096b79324eea451a70ffdf8dd3eb80097410a47f Mon Sep 17 00:00:00 2001 From: Eyal Posener Date: Mon, 15 May 2017 19:32:59 +0300 Subject: [PATCH] Add global flags for command Fixes #24 --- command.go | 38 ++++++++++++++++++++++++-------------- complete_test.go | 38 ++++++++++++++++---------------------- gocomplete/complete.go | 2 +- readme.md | 13 +++++++++---- 4 files changed, 50 insertions(+), 41 deletions(-) diff --git a/command.go b/command.go index bd5dcc4..eeeb9e0 100644 --- a/command.go +++ b/command.go @@ -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)...) } diff --git a/complete_test.go b/complete_test.go index 5cc4be0..cd61ceb 100644 --- a/complete_test.go +++ b/complete_test.go @@ -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 ", diff --git a/gocomplete/complete.go b/gocomplete/complete.go index 5c24417..a44d8aa 100644 --- a/gocomplete/complete.go +++ b/gocomplete/complete.go @@ -181,7 +181,7 @@ func main() { "fix": fix, "version": version, }, - Flags: complete.Flags{ + GlobalFlags: complete.Flags{ "-h": complete.PredictNothing, }, } diff --git a/readme.md b/readme.md index 66e327a..274474d 100644 --- a/readme.md +++ b/readme.md @@ -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.