From d3c10410d6ceaf0874b5a646f02c88170be33d4c Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Wed, 23 Aug 2017 13:58:31 -0700 Subject: [PATCH] Fix a subcommand matching This PR fixes an issue where a subcommand matches the current set of commands being examined. Fixes issue https://github.com/posener/complete/issues/46 --- command.go | 6 ++++ complete_test.go | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/command.go b/command.go index eeeb9e0..de501d5 100644 --- a/command.go +++ b/command.go @@ -74,6 +74,12 @@ func (c *Command) predict(a Args) (options []string, only bool) { return } } + + // We matched so stop searching. Continuing to search can accidentally + // match a subcommand with current set of commands, see issue #46. + if subCommandFound { + break + } } // if last completed word is a global flag that we need to complete diff --git a/complete_test.go b/complete_test.go index 135c6ad..1a42da1 100644 --- a/complete_test.go +++ b/complete_test.go @@ -184,6 +184,81 @@ func TestCompleter_Complete(t *testing.T) { } } +func TestCompleter_Complete_SharedPrefix(t *testing.T) { + t.Parallel() + initTests() + + c := Command{ + Sub: Commands{ + "status": { + Flags: Flags{ + "-f3": PredictNothing, + }, + }, + "job": { + Sub: Commands{ + "status": { + Flags: Flags{ + "-f4": PredictNothing, + }, + }, + }, + }, + }, + Flags: Flags{ + "-o": PredictFiles("*.txt"), + }, + GlobalFlags: Flags{ + "-h": PredictNothing, + "-global1": PredictAnything, + }, + } + + tests := []struct { + args string + want []string + }{ + { + args: "", + want: []string{"status", "job", "-h", "-global1", "-o"}, + }, + { + args: "-", + want: []string{"-h", "-global1", "-o"}, + }, + { + args: "j", + want: []string{"job"}, + }, + { + args: "job ", + want: []string{"-h", "-global1", "status"}, + }, + { + args: "job status ", + want: []string{"-f4", "-h", "-global1"}, + }, + } + + for _, tt := range tests { + t.Run(tt.args, func(t *testing.T) { + + tt.args = "cmd " + tt.args + os.Setenv(envComplete, tt.args) + line, _ := getLine() + + got := c.Predict(newArgs(line)) + + sort.Strings(tt.want) + sort.Strings(got) + + if !equalSlices(got, tt.want) { + t.Errorf("failed '%s'\ngot = %s\nwant: %s", t.Name(), got, tt.want) + } + }) + } +} + func equalSlices(a, b []string) bool { if len(a) != len(b) { return false