From 59e6151c5bb3277462aa4c484af0e6f853df662d Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Wed, 23 Aug 2017 13:16:52 -0700 Subject: Allow restricting completion of flags This PR allows a command to specify that flags should only be completed when a prefix is present. The motivation behind this is to have the initial complation to prefer displaying argument completions and only display flag completions when the user enters "hyphen ". --- command.go | 19 ++++++++++++++++--- complete_test.go | 31 +++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/command.go b/command.go index 9614269..3566b56 100644 --- a/command.go +++ b/command.go @@ -1,6 +1,10 @@ package complete -import "github.com/posener/complete/match" +import ( + "strings" + + "github.com/posener/complete/match" +) // Command represents a command line // It holds the data that enables auto completion of command line @@ -19,6 +23,11 @@ type Command struct { // Global flags that can appear also after a sub command. GlobalFlags Flags + // FlagsRequirePrefix requires that the prefix is provided before flags are + // autocompleted. This allows completion to only display completions for + // arguments if for example no hypen is provided. + FlagsRequirePrefix string + // Args are extra arguments that the command accepts, those who are // given without any flag before. Args Predictor @@ -86,7 +95,9 @@ func (c *Command) predict(a Args) (options []string, only bool) { return predictor.Predict(a), true } - options = append(options, c.GlobalFlags.Predict(a)...) + if c.FlagsRequirePrefix == "" || strings.HasPrefix(a.Last, c.FlagsRequirePrefix) { + options = append(options, c.GlobalFlags.Predict(a)...) + } // if a sub command was entered, we won't add the parent command // completions and we return here. @@ -101,7 +112,9 @@ func (c *Command) predict(a Args) (options []string, only bool) { } options = append(options, c.Sub.Predict(a)...) - options = append(options, c.Flags.Predict(a)...) + if c.FlagsRequirePrefix == "" || strings.HasPrefix(a.Last, c.FlagsRequirePrefix) { + 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 1a42da1..768f2c8 100644 --- a/complete_test.go +++ b/complete_test.go @@ -25,6 +25,13 @@ func TestCompleter_Complete(t *testing.T) { }, Args: PredictFiles("*.md"), }, + "sub3": { + Flags: Flags{ + "-flag4": PredictAnything, + "-flag5": PredictNothing, + }, + FlagsRequirePrefix: "-", + }, }, Flags: Flags{ "-o": PredictFiles("*.txt"), @@ -41,7 +48,7 @@ func TestCompleter_Complete(t *testing.T) { }{ { args: "", - want: []string{"sub1", "sub2", "-h", "-global1", "-o"}, + want: []string{"sub1", "sub2", "sub3", "-h", "-global1", "-o"}, }, { args: "-", @@ -49,7 +56,7 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "-h ", - want: []string{"sub1", "sub2", "-h", "-global1", "-o"}, + want: []string{"sub1", "sub2", "sub3", "-h", "-global1", "-o"}, }, { args: "-global1 ", // global1 is known follow flag @@ -57,7 +64,7 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "sub", - want: []string{"sub1", "sub2"}, + want: []string{"sub1", "sub2", "sub3"}, }, { args: "sub1", @@ -67,6 +74,10 @@ func TestCompleter_Complete(t *testing.T) { args: "sub2", want: []string{"sub2"}, }, + { + args: "sub3", + want: []string{"sub3"}, + }, { args: "sub1 ", want: []string{"-flag1", "-flag2", "-h", "-global1"}, @@ -107,13 +118,21 @@ func TestCompleter_Complete(t *testing.T) { args: "sub1 -flag2 ", want: []string{"-flag1", "-flag2", "-h", "-global1"}, }, + { + args: "sub3 ", + want: []string{"-h", "-global1"}, + }, + { + args: "sub3 -", + want: []string{"-flag4", "-flag5", "-h", "-global1"}, + }, { args: "-no-such-flag", want: []string{}, }, { args: "-no-such-flag ", - want: []string{"sub1", "sub2", "-h", "-global1", "-o"}, + want: []string{"sub1", "sub2", "sub3", "-h", "-global1", "-o"}, }, { args: "no-such-command", @@ -121,7 +140,7 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "no-such-command ", - want: []string{"sub1", "sub2", "-h", "-global1", "-o"}, + want: []string{"sub1", "sub2", "sub3", "-h", "-global1", "-o"}, }, { args: "-o ", @@ -149,7 +168,7 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "-o ./readme.md ", - want: []string{"sub1", "sub2", "-h", "-global1", "-o"}, + want: []string{"sub1", "sub2", "sub3", "-h", "-global1", "-o"}, }, { args: "-o sub2 -flag3 ", -- cgit v1.2.3 From 97340ccc2198ce94214c480eddf0fb82cb8bb14f Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Thu, 24 Aug 2017 13:22:31 -0700 Subject: Default to hiding flags that start with hyphen unless last arg has a hyphen --- command.go | 27 +++++++++++---------------- complete_test.go | 43 ++++++++++++++++--------------------------- 2 files changed, 27 insertions(+), 43 deletions(-) diff --git a/command.go b/command.go index 3566b56..c3e7e97 100644 --- a/command.go +++ b/command.go @@ -1,10 +1,6 @@ package complete -import ( - "strings" - - "github.com/posener/complete/match" -) +import "github.com/posener/complete/match" // Command represents a command line // It holds the data that enables auto completion of command line @@ -23,11 +19,6 @@ type Command struct { // Global flags that can appear also after a sub command. GlobalFlags Flags - // FlagsRequirePrefix requires that the prefix is provided before flags are - // autocompleted. This allows completion to only display completions for - // arguments if for example no hypen is provided. - FlagsRequirePrefix string - // Args are extra arguments that the command accepts, those who are // given without any flag before. Args Predictor @@ -58,6 +49,14 @@ type Flags map[string]Predictor // Predict completion of flags names according to command line arguments func (f Flags) Predict(a Args) (prediction []string) { for flag := range f { + // If the flag starts with a hyphen, we avoid emiting the prediction + // unless the last typed arg contains a hyphen as well. + flagHyphenStart := len(flag) != 0 && flag[0] == '-' + lastHyphenStart := len(a.Last) != 0 && a.Last[0] == '-' + if flagHyphenStart && !lastHyphenStart { + continue + } + if match.Prefix(flag, a.Last) { prediction = append(prediction, flag) } @@ -95,9 +94,7 @@ func (c *Command) predict(a Args) (options []string, only bool) { return predictor.Predict(a), true } - if c.FlagsRequirePrefix == "" || strings.HasPrefix(a.Last, c.FlagsRequirePrefix) { - options = append(options, c.GlobalFlags.Predict(a)...) - } + options = append(options, c.GlobalFlags.Predict(a)...) // if a sub command was entered, we won't add the parent command // completions and we return here. @@ -112,9 +109,7 @@ func (c *Command) predict(a Args) (options []string, only bool) { } options = append(options, c.Sub.Predict(a)...) - if c.FlagsRequirePrefix == "" || strings.HasPrefix(a.Last, c.FlagsRequirePrefix) { - options = append(options, c.Flags.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 768f2c8..68efb0f 100644 --- a/complete_test.go +++ b/complete_test.go @@ -25,13 +25,6 @@ func TestCompleter_Complete(t *testing.T) { }, Args: PredictFiles("*.md"), }, - "sub3": { - Flags: Flags{ - "-flag4": PredictAnything, - "-flag5": PredictNothing, - }, - FlagsRequirePrefix: "-", - }, }, Flags: Flags{ "-o": PredictFiles("*.txt"), @@ -48,7 +41,7 @@ func TestCompleter_Complete(t *testing.T) { }{ { args: "", - want: []string{"sub1", "sub2", "sub3", "-h", "-global1", "-o"}, + want: []string{"sub1", "sub2"}, }, { args: "-", @@ -56,7 +49,7 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "-h ", - want: []string{"sub1", "sub2", "sub3", "-h", "-global1", "-o"}, + want: []string{"sub1", "sub2"}, }, { args: "-global1 ", // global1 is known follow flag @@ -64,7 +57,7 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "sub", - want: []string{"sub1", "sub2", "sub3"}, + want: []string{"sub1", "sub2"}, }, { args: "sub1", @@ -75,16 +68,16 @@ func TestCompleter_Complete(t *testing.T) { want: []string{"sub2"}, }, { - args: "sub3", - want: []string{"sub3"}, + args: "sub1 ", + want: []string{}, }, { - args: "sub1 ", + args: "sub1 -", want: []string{"-flag1", "-flag2", "-h", "-global1"}, }, { args: "sub2 ", - want: []string{"./", "dir/", "outer/", "readme.md", "-flag2", "-flag3", "-h", "-global1"}, + want: []string{"./", "dir/", "outer/", "readme.md"}, }, { args: "sub2 ./", @@ -100,7 +93,7 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "sub2 -flag2 ", - want: []string{"./", "dir/", "outer/", "readme.md", "-flag2", "-flag3", "-h", "-global1"}, + want: []string{"./", "dir/", "outer/", "readme.md"}, }, { args: "sub1 -fl", @@ -115,24 +108,20 @@ func TestCompleter_Complete(t *testing.T) { want: []string{}, // flag1 is unknown follow flag }, { - args: "sub1 -flag2 ", + args: "sub1 -flag2 -", want: []string{"-flag1", "-flag2", "-h", "-global1"}, }, - { - args: "sub3 ", - want: []string{"-h", "-global1"}, - }, - { - args: "sub3 -", - want: []string{"-flag4", "-flag5", "-h", "-global1"}, - }, { args: "-no-such-flag", want: []string{}, }, { args: "-no-such-flag ", - want: []string{"sub1", "sub2", "sub3", "-h", "-global1", "-o"}, + want: []string{"sub1", "sub2"}, + }, + { + args: "-no-such-flag -", + want: []string{"-h", "-global1", "-o"}, }, { args: "no-such-command", @@ -140,7 +129,7 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "no-such-command ", - want: []string{"sub1", "sub2", "sub3", "-h", "-global1", "-o"}, + want: []string{"sub1", "sub2"}, }, { args: "-o ", @@ -168,7 +157,7 @@ func TestCompleter_Complete(t *testing.T) { }, { args: "-o ./readme.md ", - want: []string{"sub1", "sub2", "sub3", "-h", "-global1", "-o"}, + want: []string{"sub1", "sub2"}, }, { args: "-o sub2 -flag3 ", -- cgit v1.2.3 From 2b9ace50d6baef8456aa9cfb65b961b00888a54c Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Thu, 24 Aug 2017 17:37:26 -0700 Subject: Fix tests from rebase --- complete_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/complete_test.go b/complete_test.go index 68efb0f..ba4df4a 100644 --- a/complete_test.go +++ b/complete_test.go @@ -228,7 +228,7 @@ func TestCompleter_Complete_SharedPrefix(t *testing.T) { }{ { args: "", - want: []string{"status", "job", "-h", "-global1", "-o"}, + want: []string{"status", "job"}, }, { args: "-", @@ -240,10 +240,18 @@ func TestCompleter_Complete_SharedPrefix(t *testing.T) { }, { args: "job ", - want: []string{"-h", "-global1", "status"}, + want: []string{"status"}, + }, + { + args: "job -", + want: []string{"-h", "-global1"}, }, { args: "job status ", + want: []string{}, + }, + { + args: "job status -", want: []string{"-f4", "-h", "-global1"}, }, } -- cgit v1.2.3 From 91e5b1f44aaf66170b8e890442035731cd4aa60d Mon Sep 17 00:00:00 2001 From: Eyal Posener Date: Fri, 25 Aug 2017 09:41:42 +0300 Subject: lint: fix typo --- command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command.go b/command.go index c3e7e97..6de48e9 100644 --- a/command.go +++ b/command.go @@ -49,7 +49,7 @@ type Flags map[string]Predictor // Predict completion of flags names according to command line arguments func (f Flags) Predict(a Args) (prediction []string) { for flag := range f { - // If the flag starts with a hyphen, we avoid emiting the prediction + // If the flag starts with a hyphen, we avoid emitting the prediction // unless the last typed arg contains a hyphen as well. flagHyphenStart := len(flag) != 0 && flag[0] == '-' lastHyphenStart := len(a.Last) != 0 && a.Last[0] == '-' -- cgit v1.2.3