Skip to content

Commit

Permalink
Fish completion using Go completion
Browse files Browse the repository at this point in the history
Signed-off-by: Marc Khouzam <marc.khouzam@montreal.ca>
  • Loading branch information
marckhouzam committed Feb 28, 2020
1 parent f173c83 commit 6901007
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 0 deletions.
115 changes: 115 additions & 0 deletions fish_completions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package cobra

import (
"bytes"
"fmt"
"io"
"os"
)

func genFishComp(buf *bytes.Buffer, name string, includeDesc bool) {
compCmd := CompRequestCmd
if includeDesc {
compCmd = CompWithDescRequestCmd
}
buf.WriteString(fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name))
buf.WriteString(fmt.Sprintf(`
function __%[1]s_debug
set file "$BASH_COMP_DEBUG_FILE"
if test -n "$file"
echo "$argv" >> $file
end
end
function __%[1]s_handle_completion
__%[1]s_debug "Starting __%[1]s_handle_completion with: $argv"
set -l args (string split " " "$argv")
__%[1]s_debug "last arg: $args[-1]"
set -l emptyArg ""
if test -z "$args[-1]"
__%[1]s_debug "Setting emptyArg"
set emptyArg \"\"
end
__%[1]s_debug "emptyArg: $emptyArg"
set requestComp "$args[1] %[2]s $args[2..-1] $emptyArg"
__%[1]s_debug "Calling $requestComp"
eval $requestComp 2> /dev/null
end
function __%[1]s_get_completions
# Use the cache if possible
if not set -q __%[1]s_cache_completions
set -g __%[1]s_cache_completions (__%[1]s_handle_completion (commandline))
__%[1]s_debug "Populating completion cache with: $__%[1]s_cache_completions"
end
set -l directive (string sub --start 2 $__%[1]s_cache_completions[-1])
set -l comps $__%[1]s_cache_completions[1..-2]
__%[1]s_debug "Completions are: $comps"
__%[1]s_debug "Directive is: $directive"
set -l compErr (math (math $directive / %[3]d) %% 2)
if test $compErr -eq 1
return 0
end
set -l nospace (math (math $directive / %[4]d) %% 2)
set -l nofiles (math (math $directive / %[5]d) %% 2)
__%[1]s_debug "nospace: $nospace, nofiles: $nofiles"
for i in $comps
printf "%%s\n" $i
end
if test (count $comps) -eq 1; and test $nospace -ne 0
# To support the "nospace" directive we trick the shell
# by outputting an extra, longer completion.
printf "%%s\n" $comps[1].
end
# Return true if no file completion should be done
test (count $comps) -gt 0; or test $nofiles -ne 0
end
# Remove any pre-existing completions for the program since we will be handling all of them
# TODO this cleanup is not sufficient. Fish completions are only loaded once the user triggers
# them, so the below deletion will not work as it is run too early. What else can we do?
complete -c %[1]s -e
# The order in which the below two lines are defined is very important so that the cache gets
# properly cleared at the very beginning of completions processing
#
# This completion will be run second as complete commands are added FILO. It should use the cache.
# It provides file completion choices when appropriate.
complete -c %[1]s -n 'not __%[1]s_get_completions'
# This completion will be run first as complete commands are added FILO. It first clears the cache.
# It provides the program's completion choices.
complete -c %[1]s -n 'set -e __%[1]s_cache_completions; __%[1]s_get_completions' -f -a '(__%[1]s_get_completions)'
`, name, compCmd, BashCompDirectiveError, BashCompDirectiveNoSpace, BashCompDirectiveNoFileComp))
}

// GenFishCompletion generates fish completion file and writes to the passed writer.
func (c *Command) GenFishCompletion(w io.Writer, includeDesc bool) error {
buf := new(bytes.Buffer)
genFishComp(buf, c.Name(), includeDesc)
_, err := buf.WriteTo(w)
return err
}

// GenFishCompletionFile generates fish completion file.
func (c *Command) GenFishCompletionFile(filename string, includeDesc bool) error {
outFile, err := os.Create(filename)
if err != nil {
return err
}
defer outFile.Close()

return c.GenFishCompletion(outFile, includeDesc)
}
7 changes: 7 additions & 0 deletions fish_completions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Generating Fish Completions for your own cobra.Command

Cobra supports native Fish completions generated from the root `cobra.Command`. You can use the `command.GenFishCompletion()` or `command.GenFishCompletionFile()` functions. You must provide these functions with a parameter indicating if the completions should be annotated with a description; Cobra will provide the description automatically based on usage information. You can choose to make this option configurable by your users.

### Limitations

* Custom completions implemented using the `ValidArgsFunction` and `RegisterFlagCompletionFunc()` are supported automatically but the ones implemented in Bash scripting are not.

0 comments on commit 6901007

Please sign in to comment.