vendor/github.com/spf13/cobra/fish_completions.go
changeset 256 6d9efbef00a9
child 260 445e01aede7e
equal deleted inserted replaced
255:4f153a23adab 256:6d9efbef00a9
       
     1 package cobra
       
     2 
       
     3 import (
       
     4 	"bytes"
       
     5 	"fmt"
       
     6 	"io"
       
     7 	"os"
       
     8 	"strings"
       
     9 )
       
    10 
       
    11 func genFishComp(buf io.StringWriter, name string, includeDesc bool) {
       
    12 	// Variables should not contain a '-' or ':' character
       
    13 	nameForVar := name
       
    14 	nameForVar = strings.Replace(nameForVar, "-", "_", -1)
       
    15 	nameForVar = strings.Replace(nameForVar, ":", "_", -1)
       
    16 
       
    17 	compCmd := ShellCompRequestCmd
       
    18 	if !includeDesc {
       
    19 		compCmd = ShellCompNoDescRequestCmd
       
    20 	}
       
    21 	WriteStringAndCheck(buf, fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name))
       
    22 	WriteStringAndCheck(buf, fmt.Sprintf(`
       
    23 function __%[1]s_debug
       
    24     set -l file "$BASH_COMP_DEBUG_FILE"
       
    25     if test -n "$file"
       
    26         echo "$argv" >> $file
       
    27     end
       
    28 end
       
    29 
       
    30 function __%[1]s_perform_completion
       
    31     __%[1]s_debug "Starting __%[1]s_perform_completion"
       
    32 
       
    33     # Extract all args except the last one
       
    34     set -l args (commandline -opc)
       
    35     # Extract the last arg and escape it in case it is a space
       
    36     set -l lastArg (string escape -- (commandline -ct))
       
    37 
       
    38     __%[1]s_debug "args: $args"
       
    39     __%[1]s_debug "last arg: $lastArg"
       
    40 
       
    41     set -l requestComp "$args[1] %[3]s $args[2..-1] $lastArg"
       
    42 
       
    43     __%[1]s_debug "Calling $requestComp"
       
    44     set -l results (eval $requestComp 2> /dev/null)
       
    45 
       
    46     # Some programs may output extra empty lines after the directive.
       
    47     # Let's ignore them or else it will break completion.
       
    48     # Ref: https://github.com/spf13/cobra/issues/1279
       
    49     for line in $results[-1..1]
       
    50         if test (string trim -- $line) = ""
       
    51             # Found an empty line, remove it
       
    52             set results $results[1..-2]
       
    53         else
       
    54             # Found non-empty line, we have our proper output
       
    55             break
       
    56         end
       
    57     end
       
    58 
       
    59     set -l comps $results[1..-2]
       
    60     set -l directiveLine $results[-1]
       
    61 
       
    62     # For Fish, when completing a flag with an = (e.g., <program> -n=<TAB>)
       
    63     # completions must be prefixed with the flag
       
    64     set -l flagPrefix (string match -r -- '-.*=' "$lastArg")
       
    65 
       
    66     __%[1]s_debug "Comps: $comps"
       
    67     __%[1]s_debug "DirectiveLine: $directiveLine"
       
    68     __%[1]s_debug "flagPrefix: $flagPrefix"
       
    69 
       
    70     for comp in $comps
       
    71         printf "%%s%%s\n" "$flagPrefix" "$comp"
       
    72     end
       
    73 
       
    74     printf "%%s\n" "$directiveLine"
       
    75 end
       
    76 
       
    77 # This function does two things:
       
    78 # - Obtain the completions and store them in the global __%[1]s_comp_results
       
    79 # - Return false if file completion should be performed
       
    80 function __%[1]s_prepare_completions
       
    81     __%[1]s_debug ""
       
    82     __%[1]s_debug "========= starting completion logic =========="
       
    83 
       
    84     # Start fresh
       
    85     set --erase __%[1]s_comp_results
       
    86 
       
    87     set -l results (__%[1]s_perform_completion)
       
    88     __%[1]s_debug "Completion results: $results"
       
    89 
       
    90     if test -z "$results"
       
    91         __%[1]s_debug "No completion, probably due to a failure"
       
    92         # Might as well do file completion, in case it helps
       
    93         return 1
       
    94     end
       
    95 
       
    96     set -l directive (string sub --start 2 $results[-1])
       
    97     set --global __%[1]s_comp_results $results[1..-2]
       
    98 
       
    99     __%[1]s_debug "Completions are: $__%[1]s_comp_results"
       
   100     __%[1]s_debug "Directive is: $directive"
       
   101 
       
   102     set -l shellCompDirectiveError %[4]d
       
   103     set -l shellCompDirectiveNoSpace %[5]d
       
   104     set -l shellCompDirectiveNoFileComp %[6]d
       
   105     set -l shellCompDirectiveFilterFileExt %[7]d
       
   106     set -l shellCompDirectiveFilterDirs %[8]d
       
   107 
       
   108     if test -z "$directive"
       
   109         set directive 0
       
   110     end
       
   111 
       
   112     set -l compErr (math (math --scale 0 $directive / $shellCompDirectiveError) %% 2)
       
   113     if test $compErr -eq 1
       
   114         __%[1]s_debug "Received error directive: aborting."
       
   115         # Might as well do file completion, in case it helps
       
   116         return 1
       
   117     end
       
   118 
       
   119     set -l filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) %% 2)
       
   120     set -l dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) %% 2)
       
   121     if test $filefilter -eq 1; or test $dirfilter -eq 1
       
   122         __%[1]s_debug "File extension filtering or directory filtering not supported"
       
   123         # Do full file completion instead
       
   124         return 1
       
   125     end
       
   126 
       
   127     set -l nospace (math (math --scale 0 $directive / $shellCompDirectiveNoSpace) %% 2)
       
   128     set -l nofiles (math (math --scale 0 $directive / $shellCompDirectiveNoFileComp) %% 2)
       
   129 
       
   130     __%[1]s_debug "nospace: $nospace, nofiles: $nofiles"
       
   131 
       
   132     # If we want to prevent a space, or if file completion is NOT disabled,
       
   133     # we need to count the number of valid completions.
       
   134     # To do so, we will filter on prefix as the completions we have received
       
   135     # may not already be filtered so as to allow fish to match on different
       
   136     # criteria than the prefix.
       
   137     if test $nospace -ne 0; or test $nofiles -eq 0
       
   138         set -l prefix (commandline -t | string escape --style=regex)
       
   139         __%[1]s_debug "prefix: $prefix"
       
   140 
       
   141         set -l completions (string match -r -- "^$prefix.*" $__%[1]s_comp_results)
       
   142         set --global __%[1]s_comp_results $completions
       
   143         __%[1]s_debug "Filtered completions are: $__%[1]s_comp_results"
       
   144 
       
   145         # Important not to quote the variable for count to work
       
   146         set -l numComps (count $__%[1]s_comp_results)
       
   147         __%[1]s_debug "numComps: $numComps"
       
   148 
       
   149         if test $numComps -eq 1; and test $nospace -ne 0
       
   150             # We must first split on \t to get rid of the descriptions to be
       
   151             # able to check what the actual completion will be.
       
   152             # We don't need descriptions anyway since there is only a single
       
   153             # real completion which the shell will expand immediately.
       
   154             set -l split (string split --max 1 \t $__%[1]s_comp_results[1])
       
   155 
       
   156             # Fish won't add a space if the completion ends with any
       
   157             # of the following characters: @=/:.,
       
   158             set -l lastChar (string sub -s -1 -- $split)
       
   159             if not string match -r -q "[@=/:.,]" -- "$lastChar"
       
   160                 # In other cases, to support the "nospace" directive we trick the shell
       
   161                 # by outputting an extra, longer completion.
       
   162                 __%[1]s_debug "Adding second completion to perform nospace directive"
       
   163                 set --global __%[1]s_comp_results $split[1] $split[1].
       
   164                 __%[1]s_debug "Completions are now: $__%[1]s_comp_results"
       
   165             end
       
   166         end
       
   167 
       
   168         if test $numComps -eq 0; and test $nofiles -eq 0
       
   169             # To be consistent with bash and zsh, we only trigger file
       
   170             # completion when there are no other completions
       
   171             __%[1]s_debug "Requesting file completion"
       
   172             return 1
       
   173         end
       
   174     end
       
   175 
       
   176     return 0
       
   177 end
       
   178 
       
   179 # Since Fish completions are only loaded once the user triggers them, we trigger them ourselves
       
   180 # so we can properly delete any completions provided by another script.
       
   181 # Only do this if the program can be found, or else fish may print some errors; besides,
       
   182 # the existing completions will only be loaded if the program can be found.
       
   183 if type -q "%[2]s"
       
   184     # The space after the program name is essential to trigger completion for the program
       
   185     # and not completion of the program name itself.
       
   186     # Also, we use '> /dev/null 2>&1' since '&>' is not supported in older versions of fish.
       
   187     complete --do-complete "%[2]s " > /dev/null 2>&1
       
   188 end
       
   189 
       
   190 # Remove any pre-existing completions for the program since we will be handling all of them.
       
   191 complete -c %[2]s -e
       
   192 
       
   193 # The call to __%[1]s_prepare_completions will setup __%[1]s_comp_results
       
   194 # which provides the program's completion choices.
       
   195 complete -c %[2]s -n '__%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results'
       
   196 
       
   197 `, nameForVar, name, compCmd,
       
   198 		ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
       
   199 		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
       
   200 }
       
   201 
       
   202 // GenFishCompletion generates fish completion file and writes to the passed writer.
       
   203 func (c *Command) GenFishCompletion(w io.Writer, includeDesc bool) error {
       
   204 	buf := new(bytes.Buffer)
       
   205 	genFishComp(buf, c.Name(), includeDesc)
       
   206 	_, err := buf.WriteTo(w)
       
   207 	return err
       
   208 }
       
   209 
       
   210 // GenFishCompletionFile generates fish completion file.
       
   211 func (c *Command) GenFishCompletionFile(filename string, includeDesc bool) error {
       
   212 	outFile, err := os.Create(filename)
       
   213 	if err != nil {
       
   214 		return err
       
   215 	}
       
   216 	defer outFile.Close()
       
   217 
       
   218 	return c.GenFishCompletion(outFile, includeDesc)
       
   219 }