From 79da56c1f66edadc87fbe725e971470c51f16e40 Mon Sep 17 00:00:00 2001 From: Marc Khouzam Date: Wed, 25 Mar 2020 19:51:32 -0400 Subject: [PATCH] feat(comp): Move fish script to complete.go The fish script should eventually be provided by Cobra. The complete.go file holds the code that should be pushed down to Cobra, so the fish script code belongs there. Signed-off-by: Marc Khouzam --- cmd/helm/completion.go | 154 +++----------------------------- internal/completion/complete.go | 147 ++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 143 deletions(-) diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go index f46ff680e..f733e6c0a 100644 --- a/cmd/helm/completion.go +++ b/cmd/helm/completion.go @@ -251,153 +251,21 @@ __helm_bash_source <(__helm_convert_bash_to_zsh) } func runCompletionFish(out io.Writer, cmd *cobra.Command) error { - compCmd := completion.CompWithDescRequestCmd - if completionNoDesc { - compCmd = completion.CompRequestCmd - } - fishScript := fmt.Sprintf(`# fish completion for helm -*- shell-script -*- - -function __helm_debug - set file "$BASH_COMP_DEBUG_FILE" - if test -n "$file" - echo "$argv" >> $file - end -end - -function __helm_comp_perform - __helm_debug "Starting __helm_comp_perform with: $argv" - - set args (string split -- " " "$argv") - set lastArg "$args[-1]" - - __helm_debug "args: $args" - __helm_debug "last arg: $lastArg" - - set emptyArg "" - if test -z "$lastArg" - __helm_debug "Setting emptyArg" - set emptyArg \"\" - end - __helm_debug "emptyArg: $emptyArg" - - set requestComp "$args[1] %[1]s $args[2..-1] $emptyArg" - __helm_debug "Calling: $requestComp" - - set results (eval $requestComp 2> /dev/null) - set comps $results[1..-2] - set directiveLine $results[-1] - - # For Fish, when completing a flag with an = (e.g., helm -n=) - # completions must be prefixed with the flag - set flagPrefix (string match -r -- '-.*=' "$lastArg") - - __helm_debug "Comps are: $comps" - __helm_debug "DirectiveLine is: $directiveLine" - __helm_debug "flagPrefix is: $flagPrefix" - - for comp in $comps - printf "%%s%%s\n" "$flagPrefix" "$comp" - end - printf "%%s\n" "$directiveLine" -end - -# This function does three things: -# 1- Obtain the completions and store them in the global __helm_comp_results -# 2- Set the __helm_comp_do_file_comp flag if file completion should be performed -# and unset it otherwise -# 3- Return true if the completion results are not empty -function __helm_comp_prepare - # Start fresh - set --erase __helm_comp_do_file_comp - set --erase __helm_comp_results - - # Check if the command-line is already provided. This is useful for testing. - if not set --query __helm_comp_commandLine - set __helm_comp_commandLine (commandline) - end - __helm_debug "commandLine is: $__helm_comp_commandLine" - - set results (__helm_comp_perform "$__helm_comp_commandLine") - set --erase __helm_comp_commandLine - __helm_debug "Completion results: $results" - - if test -z "$results" - __helm_debug "No completion, probably due to a failure" - # Might as well do file completion, in case it helps - set --global __helm_comp_do_file_comp 1 - return 0 - end - - set directive (string sub --start 2 $results[-1]) - set --global __helm_comp_results $results[1..-2] - - __helm_debug "Completions are: $__helm_comp_results" - __helm_debug "Directive is: $directive" - - if test -z "$directive" - set directive 0 - end - - set compErr (math (math --scale 0 $directive / %[2]d) %% 2) - if test $compErr -eq 1 - __helm_debug "Received error directive: aborting." - # Might as well do file completion, in case it helps - set --global __helm_comp_do_file_comp 1 - return 0 - end - - set nospace (math (math --scale 0 $directive / %[3]d) %% 2) - set nofiles (math (math --scale 0 $directive / %[4]d) %% 2) + err := completion.GenFishCompletion(out, completionNoDesc) - __helm_debug "nospace: $nospace, nofiles: $nofiles" - - # Important not to quote the variable for count to work - set numComps (count $__helm_comp_results) - __helm_debug "numComps: $numComps" - - if test $numComps -eq 1; and test $nospace -ne 0 - # To support the "nospace" directive we trick the shell - # by outputting an extra, longer completion. - __helm_debug "Adding second completion to perform nospace directive" - set --append __helm_comp_results $__helm_comp_results[1]. - end - - if test $numComps -eq 0; and test $nofiles -eq 0 - __helm_debug "Requesting file completion" - set --global __helm_comp_do_file_comp 1 - end - - # If we don't want file completion, we must return true even if there - # are no completions found. This is because fish will perform the last - # completion command, even if its condition is false, if no other - # completion command was triggered - return (not set --query __helm_comp_do_file_comp) -end -`, compCmd, completion.BashCompDirectiveError, completion.BashCompDirectiveNoSpace, completion.BashCompDirectiveNoFileComp) - - completeCmds := ` -# Remove any pre-existing helm completions since we will be handling all of them + // In case the user renamed the helm binary (e.g., to be able to run + // both helm2 and helm3), we hook the new binary name to the completion function + if binary := filepath.Base(os.Args[0]); binary != "helm" { + renamedBinaryHook := ` +# Hook the binary name used to call the completion command +# to the fish completion functions. This is to handle the +# case where the user renamed the helm binary complete -c %[1]s -e - -# The order in which the below two lines are defined is very important so that __helm_comp_prepare -# is called first. It is __helm_comp_prepare that sets up the __helm_comp_do_file_comp variable. -# -# This completion will be run second as complete commands are added FILO. -# It triggers file completion choices when __helm_comp_do_file_comp is set. complete -c %[1]s -n 'set --query __helm_comp_do_file_comp' - -# This completion will be run first as complete commands are added FILO. -# The call to __helm_comp_prepare will setup both __helm_comp_results abd __helm_comp_do_file_comp. -# It provides the program's completion choices. complete -c %[1]s -n '__helm_comp_prepare' -f -a '$__helm_comp_results' ` - out.Write([]byte(fishScript)) - out.Write([]byte(fmt.Sprintf(completeCmds, "helm"))) - - // In case the user renamed the helm binary (e.g., to be able to run - // both helm2 and helm3), we hook the new binary name to the completion function - if binary := filepath.Base(os.Args[0]); binary != "helm" { - out.Write([]byte(fmt.Sprintf(completeCmds, binary))) + fmt.Fprintf(out, renamedBinaryHook, binary) } - return nil + + return err } diff --git a/internal/completion/complete.go b/internal/completion/complete.go index 60ca91528..4934308d4 100644 --- a/internal/completion/complete.go +++ b/internal/completion/complete.go @@ -131,6 +131,153 @@ __helm_custom_func() `, CompRequestCmd, BashCompDirectiveError, BashCompDirectiveNoSpace, BashCompDirectiveNoFileComp) } +// GenFishCompletion returns the fish script to handle completion fully +// This should eventually be provided by Cobra +func GenFishCompletion(out io.Writer, completionNoDesc bool) error { + compCmd := CompWithDescRequestCmd + if completionNoDesc { + compCmd = CompRequestCmd + } + fishScript := fmt.Sprintf(`# fish completion for helm -*- shell-script -*- + +function __helm_debug + set file "$BASH_COMP_DEBUG_FILE" + if test -n "$file" + echo "$argv" >> $file + end +end + +function __helm_comp_perform + __helm_debug "Starting __helm_comp_perform with: $argv" + + set args (string split -- " " "$argv") + set lastArg "$args[-1]" + + __helm_debug "args: $args" + __helm_debug "last arg: $lastArg" + + set emptyArg "" + if test -z "$lastArg" + __helm_debug "Setting emptyArg" + set emptyArg \"\" + end + __helm_debug "emptyArg: $emptyArg" + + set requestComp "$args[1] %[1]s $args[2..-1] $emptyArg" + __helm_debug "Calling: $requestComp" + + set results (eval $requestComp 2> /dev/null) + set comps $results[1..-2] + set directiveLine $results[-1] + + # For Fish, when completing a flag with an = (e.g., helm -n=) + # completions must be prefixed with the flag + set flagPrefix (string match -r -- '-.*=' "$lastArg") + + __helm_debug "Comps are: $comps" + __helm_debug "DirectiveLine is: $directiveLine" + __helm_debug "flagPrefix is: $flagPrefix" + + for comp in $comps + printf "%%s%%s\n" "$flagPrefix" "$comp" + end + + printf "%%s\n" "$directiveLine" +end + +# This function does three things: +# 1- Obtain the completions and store them in the global __helm_comp_results +# 2- Set the __helm_comp_do_file_comp flag if file completion should be performed +# and unset it otherwise +# 3- Return true if the completion results are not empty +function __helm_comp_prepare + # Start fresh + set --erase __helm_comp_do_file_comp + set --erase __helm_comp_results + + # Check if the command-line is already provided. This is useful for testing. + if not set --query __helm_comp_commandLine + set __helm_comp_commandLine (commandline) + end + __helm_debug "commandLine is: $__helm_comp_commandLine" + + set results (__helm_comp_perform "$__helm_comp_commandLine") + set --erase __helm_comp_commandLine + __helm_debug "Completion results: $results" + + if test -z "$results" + __helm_debug "No completion, probably due to a failure" + # Might as well do file completion, in case it helps + set --global __helm_comp_do_file_comp 1 + return 0 + end + + set directive (string sub --start 2 $results[-1]) + set --global __helm_comp_results $results[1..-2] + + __helm_debug "Completions are: $__helm_comp_results" + __helm_debug "Directive is: $directive" + + if test -z "$directive" + set directive 0 + end + + set compErr (math (math --scale 0 $directive / %[2]d) %% 2) + if test $compErr -eq 1 + __helm_debug "Received error directive: aborting." + # Might as well do file completion, in case it helps + set --global __helm_comp_do_file_comp 1 + return 0 + end + + set nospace (math (math --scale 0 $directive / %[3]d) %% 2) + set nofiles (math (math --scale 0 $directive / %[4]d) %% 2) + + __helm_debug "nospace: $nospace, nofiles: $nofiles" + + # Important not to quote the variable for count to work + set numComps (count $__helm_comp_results) + __helm_debug "numComps: $numComps" + + if test $numComps -eq 1; and test $nospace -ne 0 + # To support the "nospace" directive we trick the shell + # by outputting an extra, longer completion. + __helm_debug "Adding second completion to perform nospace directive" + set --append __helm_comp_results $__helm_comp_results[1]. + end + + if test $numComps -eq 0; and test $nofiles -eq 0 + __helm_debug "Requesting file completion" + set --global __helm_comp_do_file_comp 1 + end + + # If we don't want file completion, we must return true even if there + # are no completions found. This is because fish will perform the last + # completion command, even if its condition is false, if no other + # completion command was triggered + return (not set --query __helm_comp_do_file_comp) +end + +# Remove any pre-existing helm completions since we will be handling all of them +complete -c helm -e + +# The order in which the below two lines are defined is very important so that __helm_comp_prepare +# is called first. It is __helm_comp_prepare that sets up the __helm_comp_do_file_comp variable. +# +# This completion will be run second as complete commands are added FILO. +# It triggers file completion choices when __helm_comp_do_file_comp is set. +complete -c helm -n 'set --query __helm_comp_do_file_comp' + +# This completion will be run first as complete commands are added FILO. +# The call to __helm_comp_prepare will setup both __helm_comp_results abd __helm_comp_do_file_comp. +# It provides the program's completion choices. +complete -c helm -n '__helm_comp_prepare' -f -a '$__helm_comp_results' +`, compCmd, BashCompDirectiveError, BashCompDirectiveNoSpace, BashCompDirectiveNoFileComp) + + out.Write([]byte(fishScript)) + return nil +} + // RegisterValidArgsFunc should be called to register a function to provide argument completion for a command func RegisterValidArgsFunc(cmd *cobra.Command, f func(cmd *cobra.Command, args []string, toComplete string) ([]string, BashCompDirective)) { if _, exists := validArgsFunctions[cmd]; exists {