gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

fish_completions.go (11480B)


      1 // Copyright 2013-2023 The Cobra Authors
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //      http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package cobra
     16 
     17 import (
     18 	"bytes"
     19 	"fmt"
     20 	"io"
     21 	"os"
     22 	"strings"
     23 )
     24 
     25 func genFishComp(buf io.StringWriter, name string, includeDesc bool) {
     26 	// Variables should not contain a '-' or ':' character
     27 	nameForVar := name
     28 	nameForVar = strings.ReplaceAll(nameForVar, "-", "_")
     29 	nameForVar = strings.ReplaceAll(nameForVar, ":", "_")
     30 
     31 	compCmd := ShellCompRequestCmd
     32 	if !includeDesc {
     33 		compCmd = ShellCompNoDescRequestCmd
     34 	}
     35 	WriteStringAndCheck(buf, fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name))
     36 	WriteStringAndCheck(buf, fmt.Sprintf(`
     37 function __%[1]s_debug
     38     set -l file "$BASH_COMP_DEBUG_FILE"
     39     if test -n "$file"
     40         echo "$argv" >> $file
     41     end
     42 end
     43 
     44 function __%[1]s_perform_completion
     45     __%[1]s_debug "Starting __%[1]s_perform_completion"
     46 
     47     # Extract all args except the last one
     48     set -l args (commandline -opc)
     49     # Extract the last arg and escape it in case it is a space
     50     set -l lastArg (string escape -- (commandline -ct))
     51 
     52     __%[1]s_debug "args: $args"
     53     __%[1]s_debug "last arg: $lastArg"
     54 
     55     # Disable ActiveHelp which is not supported for fish shell
     56     set -l requestComp "%[10]s=0 $args[1] %[3]s $args[2..-1] $lastArg"
     57 
     58     __%[1]s_debug "Calling $requestComp"
     59     set -l results (eval $requestComp 2> /dev/null)
     60 
     61     # Some programs may output extra empty lines after the directive.
     62     # Let's ignore them or else it will break completion.
     63     # Ref: https://github.com/spf13/cobra/issues/1279
     64     for line in $results[-1..1]
     65         if test (string trim -- $line) = ""
     66             # Found an empty line, remove it
     67             set results $results[1..-2]
     68         else
     69             # Found non-empty line, we have our proper output
     70             break
     71         end
     72     end
     73 
     74     set -l comps $results[1..-2]
     75     set -l directiveLine $results[-1]
     76 
     77     # For Fish, when completing a flag with an = (e.g., <program> -n=<TAB>)
     78     # completions must be prefixed with the flag
     79     set -l flagPrefix (string match -r -- '-.*=' "$lastArg")
     80 
     81     __%[1]s_debug "Comps: $comps"
     82     __%[1]s_debug "DirectiveLine: $directiveLine"
     83     __%[1]s_debug "flagPrefix: $flagPrefix"
     84 
     85     for comp in $comps
     86         printf "%%s%%s\n" "$flagPrefix" "$comp"
     87     end
     88 
     89     printf "%%s\n" "$directiveLine"
     90 end
     91 
     92 # this function limits calls to __%[1]s_perform_completion, by caching the result behind $__%[1]s_perform_completion_once_result
     93 function __%[1]s_perform_completion_once
     94     __%[1]s_debug "Starting __%[1]s_perform_completion_once"
     95 
     96     if test -n "$__%[1]s_perform_completion_once_result"
     97         __%[1]s_debug "Seems like a valid result already exists, skipping __%[1]s_perform_completion"
     98         return 0
     99     end
    100 
    101     set --global __%[1]s_perform_completion_once_result (__%[1]s_perform_completion)
    102     if test -z "$__%[1]s_perform_completion_once_result"
    103         __%[1]s_debug "No completions, probably due to a failure"
    104         return 1
    105     end
    106 
    107     __%[1]s_debug "Performed completions and set __%[1]s_perform_completion_once_result"
    108     return 0
    109 end
    110 
    111 # this function is used to clear the $__%[1]s_perform_completion_once_result variable after completions are run
    112 function __%[1]s_clear_perform_completion_once_result
    113     __%[1]s_debug ""
    114     __%[1]s_debug "========= clearing previously set __%[1]s_perform_completion_once_result variable =========="
    115     set --erase __%[1]s_perform_completion_once_result
    116     __%[1]s_debug "Succesfully erased the variable __%[1]s_perform_completion_once_result"
    117 end
    118 
    119 function __%[1]s_requires_order_preservation
    120     __%[1]s_debug ""
    121     __%[1]s_debug "========= checking if order preservation is required =========="
    122 
    123     __%[1]s_perform_completion_once
    124     if test -z "$__%[1]s_perform_completion_once_result"
    125         __%[1]s_debug "Error determining if order preservation is required"
    126         return 1
    127     end
    128 
    129     set -l directive (string sub --start 2 $__%[1]s_perform_completion_once_result[-1])
    130     __%[1]s_debug "Directive is: $directive"
    131 
    132     set -l shellCompDirectiveKeepOrder %[9]d
    133     set -l keeporder (math (math --scale 0 $directive / $shellCompDirectiveKeepOrder) %% 2)
    134     __%[1]s_debug "Keeporder is: $keeporder"
    135 
    136     if test $keeporder -ne 0
    137         __%[1]s_debug "This does require order preservation"
    138         return 0
    139     end
    140 
    141     __%[1]s_debug "This doesn't require order preservation"
    142     return 1
    143 end
    144 
    145 
    146 # This function does two things:
    147 # - Obtain the completions and store them in the global __%[1]s_comp_results
    148 # - Return false if file completion should be performed
    149 function __%[1]s_prepare_completions
    150     __%[1]s_debug ""
    151     __%[1]s_debug "========= starting completion logic =========="
    152 
    153     # Start fresh
    154     set --erase __%[1]s_comp_results
    155 
    156     __%[1]s_perform_completion_once
    157     __%[1]s_debug "Completion results: $__%[1]s_perform_completion_once_result"
    158 
    159     if test -z "$__%[1]s_perform_completion_once_result"
    160         __%[1]s_debug "No completion, probably due to a failure"
    161         # Might as well do file completion, in case it helps
    162         return 1
    163     end
    164 
    165     set -l directive (string sub --start 2 $__%[1]s_perform_completion_once_result[-1])
    166     set --global __%[1]s_comp_results $__%[1]s_perform_completion_once_result[1..-2]
    167 
    168     __%[1]s_debug "Completions are: $__%[1]s_comp_results"
    169     __%[1]s_debug "Directive is: $directive"
    170 
    171     set -l shellCompDirectiveError %[4]d
    172     set -l shellCompDirectiveNoSpace %[5]d
    173     set -l shellCompDirectiveNoFileComp %[6]d
    174     set -l shellCompDirectiveFilterFileExt %[7]d
    175     set -l shellCompDirectiveFilterDirs %[8]d
    176 
    177     if test -z "$directive"
    178         set directive 0
    179     end
    180 
    181     set -l compErr (math (math --scale 0 $directive / $shellCompDirectiveError) %% 2)
    182     if test $compErr -eq 1
    183         __%[1]s_debug "Received error directive: aborting."
    184         # Might as well do file completion, in case it helps
    185         return 1
    186     end
    187 
    188     set -l filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) %% 2)
    189     set -l dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) %% 2)
    190     if test $filefilter -eq 1; or test $dirfilter -eq 1
    191         __%[1]s_debug "File extension filtering or directory filtering not supported"
    192         # Do full file completion instead
    193         return 1
    194     end
    195 
    196     set -l nospace (math (math --scale 0 $directive / $shellCompDirectiveNoSpace) %% 2)
    197     set -l nofiles (math (math --scale 0 $directive / $shellCompDirectiveNoFileComp) %% 2)
    198 
    199     __%[1]s_debug "nospace: $nospace, nofiles: $nofiles"
    200 
    201     # If we want to prevent a space, or if file completion is NOT disabled,
    202     # we need to count the number of valid completions.
    203     # To do so, we will filter on prefix as the completions we have received
    204     # may not already be filtered so as to allow fish to match on different
    205     # criteria than the prefix.
    206     if test $nospace -ne 0; or test $nofiles -eq 0
    207         set -l prefix (commandline -t | string escape --style=regex)
    208         __%[1]s_debug "prefix: $prefix"
    209 
    210         set -l completions (string match -r -- "^$prefix.*" $__%[1]s_comp_results)
    211         set --global __%[1]s_comp_results $completions
    212         __%[1]s_debug "Filtered completions are: $__%[1]s_comp_results"
    213 
    214         # Important not to quote the variable for count to work
    215         set -l numComps (count $__%[1]s_comp_results)
    216         __%[1]s_debug "numComps: $numComps"
    217 
    218         if test $numComps -eq 1; and test $nospace -ne 0
    219             # We must first split on \t to get rid of the descriptions to be
    220             # able to check what the actual completion will be.
    221             # We don't need descriptions anyway since there is only a single
    222             # real completion which the shell will expand immediately.
    223             set -l split (string split --max 1 \t $__%[1]s_comp_results[1])
    224 
    225             # Fish won't add a space if the completion ends with any
    226             # of the following characters: @=/:.,
    227             set -l lastChar (string sub -s -1 -- $split)
    228             if not string match -r -q "[@=/:.,]" -- "$lastChar"
    229                 # In other cases, to support the "nospace" directive we trick the shell
    230                 # by outputting an extra, longer completion.
    231                 __%[1]s_debug "Adding second completion to perform nospace directive"
    232                 set --global __%[1]s_comp_results $split[1] $split[1].
    233                 __%[1]s_debug "Completions are now: $__%[1]s_comp_results"
    234             end
    235         end
    236 
    237         if test $numComps -eq 0; and test $nofiles -eq 0
    238             # To be consistent with bash and zsh, we only trigger file
    239             # completion when there are no other completions
    240             __%[1]s_debug "Requesting file completion"
    241             return 1
    242         end
    243     end
    244 
    245     return 0
    246 end
    247 
    248 # Since Fish completions are only loaded once the user triggers them, we trigger them ourselves
    249 # so we can properly delete any completions provided by another script.
    250 # Only do this if the program can be found, or else fish may print some errors; besides,
    251 # the existing completions will only be loaded if the program can be found.
    252 if type -q "%[2]s"
    253     # The space after the program name is essential to trigger completion for the program
    254     # and not completion of the program name itself.
    255     # Also, we use '> /dev/null 2>&1' since '&>' is not supported in older versions of fish.
    256     complete --do-complete "%[2]s " > /dev/null 2>&1
    257 end
    258 
    259 # Remove any pre-existing completions for the program since we will be handling all of them.
    260 complete -c %[2]s -e
    261 
    262 # this will get called after the two calls below and clear the $__%[1]s_perform_completion_once_result global
    263 complete -c %[2]s -n '__%[1]s_clear_perform_completion_once_result'
    264 # The call to __%[1]s_prepare_completions will setup __%[1]s_comp_results
    265 # which provides the program's completion choices.
    266 # If this doesn't require order preservation, we don't use the -k flag
    267 complete -c %[2]s -n 'not __%[1]s_requires_order_preservation && __%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results'
    268 # otherwise we use the -k flag
    269 complete -k -c %[2]s -n '__%[1]s_requires_order_preservation && __%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results'
    270 `, nameForVar, name, compCmd,
    271 		ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
    272 		ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name)))
    273 }
    274 
    275 // GenFishCompletion generates fish completion file and writes to the passed writer.
    276 func (c *Command) GenFishCompletion(w io.Writer, includeDesc bool) error {
    277 	buf := new(bytes.Buffer)
    278 	genFishComp(buf, c.Name(), includeDesc)
    279 	_, err := buf.WriteTo(w)
    280 	return err
    281 }
    282 
    283 // GenFishCompletionFile generates fish completion file.
    284 func (c *Command) GenFishCompletionFile(filename string, includeDesc bool) error {
    285 	outFile, err := os.Create(filename)
    286 	if err != nil {
    287 		return err
    288 	}
    289 	defer outFile.Close()
    290 
    291 	return c.GenFishCompletion(outFile, includeDesc)
    292 }