diff --git a/autocomplete.go b/autocomplete.go index 3bec625..e28af7f 100644 --- a/autocomplete.go +++ b/autocomplete.go @@ -1,7 +1,7 @@ package cli import ( - "github.com/posener/complete/cmd/install" + "github.com/posener/complete/v2/install" ) // autocompleteInstaller is an interface to be implemented to perform the diff --git a/cli.go b/cli.go index c2dbe55..e8d4df3 100644 --- a/cli.go +++ b/cli.go @@ -11,7 +11,8 @@ import ( "text/template" "github.com/armon/go-radix" - "github.com/posener/complete" + "github.com/posener/complete/v2" + "github.com/posener/complete/v2/predict" ) // CLI contains the state necessary to run subcommands and parse the @@ -106,7 +107,7 @@ type CLI struct { AutocompleteInstall string AutocompleteUninstall string AutocompleteNoDefaultFlags bool - AutocompleteGlobalFlags complete.Flags + AutocompleteGlobalFlags map[string]complete.Predictor autocompleteInstaller autocompleteInstaller // For tests // HelpFunc and HelpWriter are used to output help information, if @@ -125,7 +126,7 @@ type CLI struct { // Internal fields set automatically once sync.Once - autocomplete *complete.Complete + autocomplete complete.Command commandTree *radix.Tree commandNested bool commandHidden map[string]struct{} @@ -173,9 +174,10 @@ func (c *CLI) Run() (int, error) { // If this is a autocompletion request, satisfy it. This must be called // first before anything else since its possible to be autocompleting // -help or -version or other flags and we want to show completions - // and not actually write the help or version. - if c.Autocomplete && c.autocomplete.Complete() { - return 0, nil + // and not actually write the help or version. In case of completion + // flow, this function will perform os.Exit. + if c.Autocomplete { + complete.Complete(c.Name, &c.autocomplete) } // Just show the version and exit if instructed. @@ -402,15 +404,15 @@ func (c *CLI) initAutocomplete() { // they don't show up on every command. if !c.AutocompleteNoDefaultFlags { cmd.Flags = map[string]complete.Predictor{ - "-" + c.AutocompleteInstall: complete.PredictNothing, - "-" + c.AutocompleteUninstall: complete.PredictNothing, - "-help": complete.PredictNothing, - "-version": complete.PredictNothing, + "-" + c.AutocompleteInstall: predict.Nothing, + "-" + c.AutocompleteUninstall: predict.Nothing, + "-help": predict.Nothing, + "-version": predict.Nothing, } } - cmd.GlobalFlags = c.AutocompleteGlobalFlags + cmd.Flags = c.AutocompleteGlobalFlags - c.autocomplete = complete.New(c.Name, cmd) + c.autocomplete = cmd } // initAutocompleteSub creates the complete.Command for a subcommand with @@ -451,7 +453,7 @@ func (c *CLI) initAutocompleteSub(prefix string) complete.Command { } if cmd.Sub == nil { - cmd.Sub = complete.Commands(make(map[string]complete.Command)) + cmd.Sub = make(map[string]*complete.Command) } subCmd := c.initAutocompleteSub(fullKey) @@ -470,7 +472,7 @@ func (c *CLI) initAutocompleteSub(prefix string) complete.Command { subCmd.Flags = c.AutocompleteFlags() } - cmd.Sub[k] = subCmd + cmd.Sub[k] = &subCmd return false } diff --git a/cli_test.go b/cli_test.go index da1a640..68607be 100644 --- a/cli_test.go +++ b/cli_test.go @@ -7,16 +7,22 @@ import ( "os" "reflect" "sort" + "strconv" "strings" "testing" - "github.com/posener/complete" + "github.com/posener/complete/v2" + "github.com/posener/complete/v2/predict" ) -// envComplete is the env var that the complete library sets to specify -// it should be calculating an auto-completion. This isn't exported so we -// reproduce it here. If it changes then we'll have to update this. -const envComplete = "COMP_LINE" +// envComplete and envPoint are the env vars that the complete library sets +// to specify it should be calculating an auto-completion. This isn't +// exported so we reproduce it here. If it changes then we'll have to update +// this. +const ( + envComplete = "COMP_LINE" + envPoint = "COMP_POINT" +) func TestCLIIsHelp(t *testing.T) { testCases := []struct { @@ -1244,7 +1250,7 @@ func TestCLIAutocomplete_rootGlobalFlags(t *testing.T) { Autocomplete: true, AutocompleteGlobalFlags: map[string]complete.Predictor{ - "-tubes": complete.PredictNothing, + "-tubes": predict.Nothing, }, } @@ -1325,7 +1331,7 @@ func TestCLIAutocomplete_rootDisableDefaultFlags(t *testing.T) { Autocomplete: true, AutocompleteNoDefaultFlags: true, AutocompleteGlobalFlags: map[string]complete.Predictor{ - "-tubes": complete.PredictNothing, + "-tubes": predict.Nothing, }, } // Setup the autocomplete line @@ -1384,20 +1390,19 @@ func TestCLIAutocomplete_rootDisableDefaultFlags(t *testing.T) { func TestCLIAutocomplete_subcommandArgs(t *testing.T) { cases := []struct { - Completed []string - Last string - Expected []string + Command string + Expected []string }{ - {[]string{"foo"}, "RE", []string{"README.md"}}, - {[]string{"foo", "-go"}, "asdf", []string{"yo"}}, + {"foo RE", []string{"README.md"}}, + {"foo -go asdf", []string{"yo"}}, } for _, tc := range cases { - t.Run(tc.Last, func(t *testing.T) { + t.Run(tc.Command, func(t *testing.T) { command := new(MockCommandAutocomplete) - command.AutocompleteArgsValue = complete.PredictFiles("*") + command.AutocompleteArgsValue = predict.Files("*") command.AutocompleteFlagsValue = map[string]complete.Predictor{ - "-go": complete.PredictFunc(func(complete.Args) []string { + "-go": complete.PredictFunc(func(string) []string { return []string{"yo"} }), } @@ -1416,16 +1421,7 @@ func TestCLIAutocomplete_subcommandArgs(t *testing.T) { cli.init() // Test the autocompleter - actual := cli.autocomplete.Command.Predict(complete.Args{ - Completed: tc.Completed, - Last: tc.Last, - LastCompleted: tc.Completed[len(tc.Completed)-1], - }) - sort.Strings(actual) - - if !reflect.DeepEqual(actual, tc.Expected) { - t.Fatalf("bad prediction: %#v", actual) - } + complete.Test(t, &cli.autocomplete, tc.Command, tc.Expected) }) } } @@ -1489,6 +1485,7 @@ func TestCLISubcommand_nested(t *testing.T) { func testAutocomplete(t *testing.T, input string) func() { // This env var is used to trigger autocomplete os.Setenv(envComplete, input) + os.Setenv(envPoint, strconv.Itoa(len(input))) // Change stdout/stderr since the autocompleter writes directly to them. oldStdout := os.Stdout @@ -1505,6 +1502,7 @@ func testAutocomplete(t *testing.T, input string) func() { return func() { // Reset our env os.Unsetenv(envComplete) + os.Unsetenv(envPoint) // Reset stdout, stderr os.Stdout = oldStdout diff --git a/command.go b/command.go index bed11fa..114168c 100644 --- a/command.go +++ b/command.go @@ -1,7 +1,7 @@ package cli import ( - "github.com/posener/complete" + "github.com/posener/complete/v2" ) const ( @@ -43,7 +43,7 @@ type CommandAutocomplete interface { // AutocompleteFlags returns a mapping of supported flags and autocomplete // options for this command. The map key for the Flags map should be the // complete flag such as "-foo" or "--foo". - AutocompleteFlags() complete.Flags + AutocompleteFlags() map[string]complete.Predictor } // CommandHelpTemplate is an extension of Command that also has a function diff --git a/command_mock.go b/command_mock.go index 7a584b7..397cdd5 100644 --- a/command_mock.go +++ b/command_mock.go @@ -1,7 +1,7 @@ package cli import ( - "github.com/posener/complete" + "github.com/posener/complete/v2" ) // MockCommand is an implementation of Command that can be used for tests. @@ -39,14 +39,14 @@ type MockCommandAutocomplete struct { // Settable AutocompleteArgsValue complete.Predictor - AutocompleteFlagsValue complete.Flags + AutocompleteFlagsValue map[string]complete.Predictor } func (c *MockCommandAutocomplete) AutocompleteArgs() complete.Predictor { return c.AutocompleteArgsValue } -func (c *MockCommandAutocomplete) AutocompleteFlags() complete.Flags { +func (c *MockCommandAutocomplete) AutocompleteFlags() map[string]complete.Predictor { return c.AutocompleteFlagsValue } diff --git a/go.mod b/go.mod index 675325f..35edb7e 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,10 @@ require ( github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 github.com/bgentry/speakeasy v0.1.0 github.com/fatih/color v1.7.0 - github.com/hashicorp/go-multierror v1.0.0 // indirect github.com/mattn/go-colorable v0.0.9 // indirect github.com/mattn/go-isatty v0.0.3 - github.com/posener/complete v1.1.1 + github.com/posener/complete/v2 v2.0.1-alpha.2 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc // indirect ) + +go 1.13 diff --git a/go.sum b/go.sum index 0370875..6f536bd 100644 --- a/go.sum +++ b/go.sum @@ -2,21 +2,28 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357 h1:Rem2+U35z1QtPQc6r+WolF7yXiefXqDKyk+lN2pE164= -github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0 h1:j30noezaCfvNLcdMYSvHLv81DxYRSt1grlpseG67vhU= -github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete/v2 v2.0.1-alpha.2 h1:y/9Kv6uzksN+fxY+qVBsJpRdyDNG+rEvJ2kiPQ73XVI= +github.com/posener/complete/v2 v2.0.1-alpha.2/go.mod h1:Q5Am7Oqn7AYZ/ZeyLMtB/4R3DYQi4atyOdgD8kP1mQw= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc h1:MeuS1UDyZyFH++6vVy44PuufTeFF0d0nfI6XB87YGSk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=