From 9a2556285c0d27bd1a096e1d6ad5944b91cc5454 Mon Sep 17 00:00:00 2001 From: Toni Kangas Date: Mon, 27 Jun 2022 16:37:08 +0300 Subject: [PATCH 1/2] TEMP deps: use patched version of cobra --- go.mod | 2 ++ go.sum | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index efb9ec91..ec19c42c 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,8 @@ require ( gopkg.in/yaml.v2 v2.4.0 ) +replace github.com/spf13/cobra => github.com/kangasta/cobra v1.5.1-0.20220623204649-b02e41b5a36d + require ( github.com/ansel1/merry v1.5.0 // indirect github.com/blang/semver v3.5.1+incompatible // indirect diff --git a/go.sum b/go.sum index 21cc535f..8354b13d 100644 --- a/go.sum +++ b/go.sum @@ -121,6 +121,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kangasta/cobra v1.5.1-0.20220623204649-b02e41b5a36d h1:ivoDZoNixM9e9+L8+KA/v7eJL9et6yB1KszdhmhDdSU= +github.com/kangasta/cobra v1.5.1-0.20220623204649-b02e41b5a36d/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -200,8 +202,6 @@ github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= From 8b53aaad420f62f66e4ed1d7e3405636bc03a16b Mon Sep 17 00:00:00 2001 From: Toni Kangas Date: Tue, 21 Jun 2022 23:09:31 +0300 Subject: [PATCH 2/2] refactor(completion): use the default completion command provided by cobra This removes custom bash completion logic and custom completion escape logic as that is not supperted by completion scripts cobra generates. --- CHANGELOG.md | 5 ++ internal/commands/all/all.go | 9 ---- internal/commands/bash_completion.go | 79 ---------------------------- internal/commands/root/completion.go | 31 ----------- internal/completion/helpers.go | 13 +---- internal/completion/helpers_test.go | 42 +-------------- internal/core/core.go | 3 -- 7 files changed, 8 insertions(+), 174 deletions(-) delete mode 100644 internal/commands/bash_completion.go delete mode 100644 internal/commands/root/completion.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 003b0d5f..29956dd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- Added support for all shell completions provided by `cobra`. + +### Changed +- Remove custom bash completion logic and replace it with `completion` command provided by `cobra`. ## [1.5.0] - 2022-07-05 ### Added diff --git a/internal/commands/all/all.go b/internal/commands/all/all.go index 27f02ba3..ce95c579 100644 --- a/internal/commands/all/all.go +++ b/internal/commands/all/all.go @@ -121,15 +121,6 @@ func BuildCommands(rootCmd *cobra.Command, conf *config.Config) { commands.BuildCommand(loadbalancer.ShowCommand(), loadbalancerCommand.Cobra(), conf) // Misc - commands.BuildCommand( - &root.CompletionCommand{ - BaseCommand: commands.New( - "completion", - "Generates shell completion", - "upctl completion bash", - ), - }, rootCmd, conf, - ) commands.BuildCommand( &root.VersionCommand{ BaseCommand: commands.New( diff --git a/internal/commands/bash_completion.go b/internal/commands/bash_completion.go deleted file mode 100644 index 604e7244..00000000 --- a/internal/commands/bash_completion.go +++ /dev/null @@ -1,79 +0,0 @@ -package commands - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -// Override Cobra's custom completion function -// This fixes quoting of items with spaces -// TODO: get fixes submitted upstream, otherwise sooner or later something will break if we upgrade cobra -const goCustomCompletion = `__%[1]s_handle_go_custom_completion() -{ - __%[1]s_debug "${FUNCNAME[0]}: cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}" - - local out requestComp lastParam lastChar comp directive args - - # Prepare the command to request completions for the program. - # Calling ${words[0]} instead of directly %[1]s allows to handle aliases - args=("${words[@]:1}") - requestComp="${words[0]} __completeNoDesc ${args[@]:0:$((${#args[@]}-1))} $'$cur'" - - lastParam=${words[$((${#words[@]}-1))]} - lastChar=${lastParam:$((${#lastParam}-1)):1} - __%[1]s_debug "${FUNCNAME[0]}: lastParam ${lastParam}, lastChar ${lastChar}" - - if [ -z "${cur}" ] && [ "${lastChar}" != "=" ]; then - # If the last parameter is complete (there is a space following it) - # We add an extra empty parameter so we can indicate this to the go method. - __%[1]s_debug "${FUNCNAME[0]}: Adding extra empty parameter" - requestComp="${requestComp} \"\"" - fi - - __%[1]s_debug "${FUNCNAME[0]}: calling ${requestComp}" - # Use eval to handle any environment variables and such - out=$(eval "${requestComp}" 2>/dev/null) - - # Extract the directive integer at the very end of the output following a colon (:) - directive=${out##*:} - # Remove the directive - out=${out%%:*} - if [ "${directive}" = "${out}" ]; then - # There is not directive specified - directive=0 - fi - __%[1]s_debug "${FUNCNAME[0]}: the completion directive is: ${directive}" - __%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out[*]}" - - if [ $((directive & %[3]d)) -ne 0 ]; then - # Error code. No completion. - __%[1]s_debug "${FUNCNAME[0]}: received error from custom completion go code" - return - else - if [ $((directive & %[4]d)) -ne 0 ]; then - if [[ $(type -t compopt) = "builtin" ]]; then - __%[1]s_debug "${FUNCNAME[0]}: activating no space" - compopt -o nospace - fi - fi - if [ $((directive & %[5]d)) -ne 0 ]; then - if [[ $(type -t compopt) = "builtin" ]]; then - __%[1]s_debug "${FUNCNAME[0]}: activating no file completion" - compopt +o default - fi - fi - - local IFS=$'\n' - COMPREPLY=($out) - fi -}` - -// CustomBashCompletionFunc returns a bash completion function used by cobras bash completion generator -func CustomBashCompletionFunc(name string) string { - return fmt.Sprintf(goCustomCompletion, name, - cobra.ShellCompNoDescRequestCmd, - cobra.ShellCompDirectiveError, - cobra.ShellCompDirectiveNoSpace, - cobra.ShellCompDirectiveNoFileComp) -} diff --git a/internal/commands/root/completion.go b/internal/commands/root/completion.go deleted file mode 100644 index 27f8dceb..00000000 --- a/internal/commands/root/completion.go +++ /dev/null @@ -1,31 +0,0 @@ -package root - -import ( - "bytes" - "fmt" - - "github.com/UpCloudLtd/upcloud-cli/internal/commands" - "github.com/UpCloudLtd/upcloud-cli/internal/output" - "github.com/UpCloudLtd/upcloud-cli/internal/resolver" -) - -// CompletionCommand creates shell completion scripts -type CompletionCommand struct { - *commands.BaseCommand - resolver.CompletionResolver -} - -// ExecuteSingleArgument implements commands.SingleArgumentCommand -func (s *CompletionCommand) ExecuteSingleArgument(_ commands.Executor, arg string) (output.Output, error) { - if arg == "bash" { - completion := new(bytes.Buffer) - err := s.Cobra().Root().GenBashCompletion(completion) - - return output.Raw(completion.Bytes()), err - } - - return nil, fmt.Errorf("completion for %s is not supported", arg) -} - -// DoesNotUseServices implements commands.OfflineCommand as this command does not use services -func (s *CompletionCommand) DoesNotUseServices() {} diff --git a/internal/completion/helpers.go b/internal/completion/helpers.go index 5ac40f75..805c7c35 100644 --- a/internal/completion/helpers.go +++ b/internal/completion/helpers.go @@ -1,7 +1,6 @@ package completion import ( - "fmt" "strings" ) @@ -9,22 +8,12 @@ import ( func MatchStringPrefix(vals []string, key string, caseSensitive bool) []string { var r []string key = strings.Trim(key, "'\"") - for _, v := range vals { if (caseSensitive && strings.HasPrefix(v, key)) || (!caseSensitive && strings.HasPrefix(strings.ToLower(v), strings.ToLower(key))) || key == "" { - r = append(r, Escape(v)) + r = append(r, v) } } return r } - -// Escape escapes a string according to completion rules (?) -// in effect, this means that the string will be quoted with double quotes if it contains a space or parentheses. -func Escape(s string) string { - if strings.ContainsAny(s, ` ()`) { - return fmt.Sprintf(`"%s"`, s) - } - return s -} diff --git a/internal/completion/helpers_test.go b/internal/completion/helpers_test.go index 5e0f2f1d..32934175 100644 --- a/internal/completion/helpers_test.go +++ b/internal/completion/helpers_test.go @@ -73,11 +73,11 @@ func TestMatchStringPrefix(t *testing.T) { expected: []string{"aba", "aBa", "Aba"}, }, { - name: "escaped output", + name: "output with special characters", vals: []string{"a a ", "a(0)", "aab", "a;