forked from mirror/cobra
Fix generated bash completion for Bash 3 (OSX) (#520)
* Make preamble functions unique to command Prior to this commit the functions in the preamble had names that didn't vary based on the command for which the bash completion was generated. This meant that if you had two bash completions with differences in the preamble functions then only the last loaded function would be available. This commit prefixes all of these functions with the name of the command so that multiple cobra generated completion files won't clash. Signed-off-by: John McCabe <john@johnmccabe.net> * Fix function names in writeFlagHandler The references to the `__handle_filename_extension_flag` and `__handle_subdirs_in_dir_flag` functions in `writeFlagHandler` hadn't been updated correctly in the previous commits. Signed-off-by: John McCabe <john@johnmccabe.net> * Pass cmd into writeFlagHandler This commit passes the cmd pointer into the writeFlagHandler so that the `__handle_filename_extension_flag` and `__handle_subdirs_in_dir_flag` functions can have the `cmd.Name()` prefixed. * Update Bash completion tests Prefixes the tested `__handle_filename_extension_flag` and `__handle_subdirs_in_dir_flag` functions with the command name.
This commit is contained in:
parent
93959269ad
commit
fd32f09af1
|
@ -21,8 +21,8 @@ const (
|
||||||
|
|
||||||
func writePreamble(buf *bytes.Buffer, name string) {
|
func writePreamble(buf *bytes.Buffer, name string) {
|
||||||
buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))
|
buf.WriteString(fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name))
|
||||||
buf.WriteString(`
|
buf.WriteString(fmt.Sprintf(`
|
||||||
__debug()
|
__%[1]s_debug()
|
||||||
{
|
{
|
||||||
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
|
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
|
||||||
echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
|
echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
|
||||||
|
@ -31,13 +31,13 @@ __debug()
|
||||||
|
|
||||||
# Homebrew on Macs have version 1.3 of bash-completion which doesn't include
|
# Homebrew on Macs have version 1.3 of bash-completion which doesn't include
|
||||||
# _init_completion. This is a very minimal version of that function.
|
# _init_completion. This is a very minimal version of that function.
|
||||||
__my_init_completion()
|
__%[1]s_init_completion()
|
||||||
{
|
{
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
_get_comp_words_by_ref "$@" cur prev words cword
|
_get_comp_words_by_ref "$@" cur prev words cword
|
||||||
}
|
}
|
||||||
|
|
||||||
__index_of_word()
|
__%[1]s_index_of_word()
|
||||||
{
|
{
|
||||||
local w word=$1
|
local w word=$1
|
||||||
shift
|
shift
|
||||||
|
@ -49,7 +49,7 @@ __index_of_word()
|
||||||
index=-1
|
index=-1
|
||||||
}
|
}
|
||||||
|
|
||||||
__contains_word()
|
__%[1]s_contains_word()
|
||||||
{
|
{
|
||||||
local w word=$1; shift
|
local w word=$1; shift
|
||||||
for w in "$@"; do
|
for w in "$@"; do
|
||||||
|
@ -58,9 +58,9 @@ __contains_word()
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
__handle_reply()
|
__%[1]s_handle_reply()
|
||||||
{
|
{
|
||||||
__debug "${FUNCNAME[0]}"
|
__%[1]s_debug "${FUNCNAME[0]}"
|
||||||
case $cur in
|
case $cur in
|
||||||
-*)
|
-*)
|
||||||
if [[ $(type -t compopt) = "builtin" ]]; then
|
if [[ $(type -t compopt) = "builtin" ]]; then
|
||||||
|
@ -85,7 +85,7 @@ __handle_reply()
|
||||||
|
|
||||||
local index flag
|
local index flag
|
||||||
flag="${cur%%=*}"
|
flag="${cur%%=*}"
|
||||||
__index_of_word "${flag}" "${flags_with_completion[@]}"
|
__%[1]s_index_of_word "${flag}" "${flags_with_completion[@]}"
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
if [[ ${index} -ge 0 ]]; then
|
if [[ ${index} -ge 0 ]]; then
|
||||||
PREFIX=""
|
PREFIX=""
|
||||||
|
@ -103,7 +103,7 @@ __handle_reply()
|
||||||
|
|
||||||
# check if we are handling a flag with special work handling
|
# check if we are handling a flag with special work handling
|
||||||
local index
|
local index
|
||||||
__index_of_word "${prev}" "${flags_with_completion[@]}"
|
__%[1]s_index_of_word "${prev}" "${flags_with_completion[@]}"
|
||||||
if [[ ${index} -ge 0 ]]; then
|
if [[ ${index} -ge 0 ]]; then
|
||||||
${flags_completion[${index}]}
|
${flags_completion[${index}]}
|
||||||
return
|
return
|
||||||
|
@ -139,21 +139,21 @@ __handle_reply()
|
||||||
}
|
}
|
||||||
|
|
||||||
# The arguments should be in the form "ext1|ext2|extn"
|
# The arguments should be in the form "ext1|ext2|extn"
|
||||||
__handle_filename_extension_flag()
|
__%[1]s_handle_filename_extension_flag()
|
||||||
{
|
{
|
||||||
local ext="$1"
|
local ext="$1"
|
||||||
_filedir "@(${ext})"
|
_filedir "@(${ext})"
|
||||||
}
|
}
|
||||||
|
|
||||||
__handle_subdirs_in_dir_flag()
|
__%[1]s_handle_subdirs_in_dir_flag()
|
||||||
{
|
{
|
||||||
local dir="$1"
|
local dir="$1"
|
||||||
pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1
|
pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
__handle_flag()
|
__%[1]s_handle_flag()
|
||||||
{
|
{
|
||||||
__debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
__%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||||
|
|
||||||
# if a command required a flag, and we found it, unset must_have_one_flag()
|
# if a command required a flag, and we found it, unset must_have_one_flag()
|
||||||
local flagname=${words[c]}
|
local flagname=${words[c]}
|
||||||
|
@ -164,13 +164,13 @@ __handle_flag()
|
||||||
flagname=${flagname%%=*} # strip everything after the =
|
flagname=${flagname%%=*} # strip everything after the =
|
||||||
flagname="${flagname}=" # but put the = back
|
flagname="${flagname}=" # but put the = back
|
||||||
fi
|
fi
|
||||||
__debug "${FUNCNAME[0]}: looking for ${flagname}"
|
__%[1]s_debug "${FUNCNAME[0]}: looking for ${flagname}"
|
||||||
if __contains_word "${flagname}" "${must_have_one_flag[@]}"; then
|
if __%[1]s_contains_word "${flagname}" "${must_have_one_flag[@]}"; then
|
||||||
must_have_one_flag=()
|
must_have_one_flag=()
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# if you set a flag which only applies to this command, don't show subcommands
|
# if you set a flag which only applies to this command, don't show subcommands
|
||||||
if __contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then
|
if __%[1]s_contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then
|
||||||
commands=()
|
commands=()
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ __handle_flag()
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# skip the argument to a two word flag
|
# skip the argument to a two word flag
|
||||||
if __contains_word "${words[c]}" "${two_word_flags[@]}"; then
|
if __%[1]s_contains_word "${words[c]}" "${two_word_flags[@]}"; then
|
||||||
c=$((c+1))
|
c=$((c+1))
|
||||||
# if we are looking for a flags value, don't show commands
|
# if we are looking for a flags value, don't show commands
|
||||||
if [[ $c -eq $cword ]]; then
|
if [[ $c -eq $cword ]]; then
|
||||||
|
@ -199,13 +199,13 @@ __handle_flag()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__handle_noun()
|
__%[1]s_handle_noun()
|
||||||
{
|
{
|
||||||
__debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
__%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||||
|
|
||||||
if __contains_word "${words[c]}" "${must_have_one_noun[@]}"; then
|
if __%[1]s_contains_word "${words[c]}" "${must_have_one_noun[@]}"; then
|
||||||
must_have_one_noun=()
|
must_have_one_noun=()
|
||||||
elif __contains_word "${words[c]}" "${noun_aliases[@]}"; then
|
elif __%[1]s_contains_word "${words[c]}" "${noun_aliases[@]}"; then
|
||||||
must_have_one_noun=()
|
must_have_one_noun=()
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -213,9 +213,9 @@ __handle_noun()
|
||||||
c=$((c+1))
|
c=$((c+1))
|
||||||
}
|
}
|
||||||
|
|
||||||
__handle_command()
|
__%[1]s_handle_command()
|
||||||
{
|
{
|
||||||
__debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
__%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||||
|
|
||||||
local next_command
|
local next_command
|
||||||
if [[ -n ${last_command} ]]; then
|
if [[ -n ${last_command} ]]; then
|
||||||
|
@ -228,30 +228,30 @@ __handle_command()
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
c=$((c+1))
|
c=$((c+1))
|
||||||
__debug "${FUNCNAME[0]}: looking for ${next_command}"
|
__%[1]s_debug "${FUNCNAME[0]}: looking for ${next_command}"
|
||||||
declare -F "$next_command" >/dev/null && $next_command
|
declare -F "$next_command" >/dev/null && $next_command
|
||||||
}
|
}
|
||||||
|
|
||||||
__handle_word()
|
__%[1]s_handle_word()
|
||||||
{
|
{
|
||||||
if [[ $c -ge $cword ]]; then
|
if [[ $c -ge $cword ]]; then
|
||||||
__handle_reply
|
__%[1]s_handle_reply
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
__debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
__%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
|
||||||
if [[ "${words[c]}" == -* ]]; then
|
if [[ "${words[c]}" == -* ]]; then
|
||||||
__handle_flag
|
__%[1]s_handle_flag
|
||||||
elif __contains_word "${words[c]}" "${commands[@]}"; then
|
elif __%[1]s_contains_word "${words[c]}" "${commands[@]}"; then
|
||||||
__handle_command
|
__%[1]s_handle_command
|
||||||
elif [[ $c -eq 0 ]] && __contains_word "$(basename "${words[c]}")" "${commands[@]}"; then
|
elif [[ $c -eq 0 ]] && __%[1]s_contains_word "$(basename "${words[c]}")" "${commands[@]}"; then
|
||||||
__handle_command
|
__%[1]s_handle_command
|
||||||
else
|
else
|
||||||
__handle_noun
|
__%[1]s_handle_noun
|
||||||
fi
|
fi
|
||||||
__handle_word
|
__%[1]s_handle_word
|
||||||
}
|
}
|
||||||
|
|
||||||
`)
|
`, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
func writePostscript(buf *bytes.Buffer, name string) {
|
func writePostscript(buf *bytes.Buffer, name string) {
|
||||||
|
@ -263,7 +263,7 @@ func writePostscript(buf *bytes.Buffer, name string) {
|
||||||
if declare -F _init_completion >/dev/null 2>&1; then
|
if declare -F _init_completion >/dev/null 2>&1; then
|
||||||
_init_completion -s || return
|
_init_completion -s || return
|
||||||
else
|
else
|
||||||
__my_init_completion -n "=" || return
|
__%[1]s_init_completion -n "=" || return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local c=0
|
local c=0
|
||||||
|
@ -272,13 +272,13 @@ func writePostscript(buf *bytes.Buffer, name string) {
|
||||||
local local_nonpersistent_flags=()
|
local local_nonpersistent_flags=()
|
||||||
local flags_with_completion=()
|
local flags_with_completion=()
|
||||||
local flags_completion=()
|
local flags_completion=()
|
||||||
local commands=("%s")
|
local commands=("%[1]s")
|
||||||
local must_have_one_flag=()
|
local must_have_one_flag=()
|
||||||
local must_have_one_noun=()
|
local must_have_one_noun=()
|
||||||
local last_command
|
local last_command
|
||||||
local nouns=()
|
local nouns=()
|
||||||
|
|
||||||
__handle_word
|
__%[1]s_handle_word
|
||||||
}
|
}
|
||||||
|
|
||||||
`, name))
|
`, name))
|
||||||
|
@ -303,7 +303,7 @@ func writeCommands(buf *bytes.Buffer, cmd *Command) {
|
||||||
buf.WriteString("\n")
|
buf.WriteString("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string) {
|
func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]string, cmd *Command) {
|
||||||
for key, value := range annotations {
|
for key, value := range annotations {
|
||||||
switch key {
|
switch key {
|
||||||
case BashCompFilenameExt:
|
case BashCompFilenameExt:
|
||||||
|
@ -311,7 +311,7 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
|
||||||
|
|
||||||
var ext string
|
var ext string
|
||||||
if len(value) > 0 {
|
if len(value) > 0 {
|
||||||
ext = "__handle_filename_extension_flag " + strings.Join(value, "|")
|
ext = fmt.Sprintf("__%s_handle_filename_extension_flag ", cmd.Name()) + strings.Join(value, "|")
|
||||||
} else {
|
} else {
|
||||||
ext = "_filedir"
|
ext = "_filedir"
|
||||||
}
|
}
|
||||||
|
@ -329,7 +329,7 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
|
||||||
|
|
||||||
var ext string
|
var ext string
|
||||||
if len(value) == 1 {
|
if len(value) == 1 {
|
||||||
ext = "__handle_subdirs_in_dir_flag " + value[0]
|
ext = fmt.Sprintf("__%s_handle_subdirs_in_dir_flag ", cmd.Name()) + value[0]
|
||||||
} else {
|
} else {
|
||||||
ext = "_filedir -d"
|
ext = "_filedir -d"
|
||||||
}
|
}
|
||||||
|
@ -338,7 +338,7 @@ func writeFlagHandler(buf *bytes.Buffer, name string, annotations map[string][]s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag) {
|
func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) {
|
||||||
name := flag.Shorthand
|
name := flag.Shorthand
|
||||||
format := " "
|
format := " "
|
||||||
if len(flag.NoOptDefVal) == 0 {
|
if len(flag.NoOptDefVal) == 0 {
|
||||||
|
@ -346,10 +346,10 @@ func writeShortFlag(buf *bytes.Buffer, flag *pflag.Flag) {
|
||||||
}
|
}
|
||||||
format += "flags+=(\"-%s\")\n"
|
format += "flags+=(\"-%s\")\n"
|
||||||
buf.WriteString(fmt.Sprintf(format, name))
|
buf.WriteString(fmt.Sprintf(format, name))
|
||||||
writeFlagHandler(buf, "-"+name, flag.Annotations)
|
writeFlagHandler(buf, "-"+name, flag.Annotations, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeFlag(buf *bytes.Buffer, flag *pflag.Flag) {
|
func writeFlag(buf *bytes.Buffer, flag *pflag.Flag, cmd *Command) {
|
||||||
name := flag.Name
|
name := flag.Name
|
||||||
format := " flags+=(\"--%s"
|
format := " flags+=(\"--%s"
|
||||||
if len(flag.NoOptDefVal) == 0 {
|
if len(flag.NoOptDefVal) == 0 {
|
||||||
|
@ -357,7 +357,7 @@ func writeFlag(buf *bytes.Buffer, flag *pflag.Flag) {
|
||||||
}
|
}
|
||||||
format += "\")\n"
|
format += "\")\n"
|
||||||
buf.WriteString(fmt.Sprintf(format, name))
|
buf.WriteString(fmt.Sprintf(format, name))
|
||||||
writeFlagHandler(buf, "--"+name, flag.Annotations)
|
writeFlagHandler(buf, "--"+name, flag.Annotations, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) {
|
func writeLocalNonPersistentFlag(buf *bytes.Buffer, flag *pflag.Flag) {
|
||||||
|
@ -383,9 +383,9 @@ func writeFlags(buf *bytes.Buffer, cmd *Command) {
|
||||||
if nonCompletableFlag(flag) {
|
if nonCompletableFlag(flag) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
writeFlag(buf, flag)
|
writeFlag(buf, flag, cmd)
|
||||||
if len(flag.Shorthand) > 0 {
|
if len(flag.Shorthand) > 0 {
|
||||||
writeShortFlag(buf, flag)
|
writeShortFlag(buf, flag, cmd)
|
||||||
}
|
}
|
||||||
if localNonPersistentFlags.Lookup(flag.Name) != nil {
|
if localNonPersistentFlags.Lookup(flag.Name) != nil {
|
||||||
writeLocalNonPersistentFlag(buf, flag)
|
writeLocalNonPersistentFlag(buf, flag)
|
||||||
|
@ -395,9 +395,9 @@ func writeFlags(buf *bytes.Buffer, cmd *Command) {
|
||||||
if nonCompletableFlag(flag) {
|
if nonCompletableFlag(flag) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
writeFlag(buf, flag)
|
writeFlag(buf, flag, cmd)
|
||||||
if len(flag.Shorthand) > 0 {
|
if len(flag.Shorthand) > 0 {
|
||||||
writeShortFlag(buf, flag)
|
writeShortFlag(buf, flag, cmd)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package cobra
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -146,11 +147,11 @@ func TestBashCompletions(t *testing.T) {
|
||||||
// check for filename extension flags
|
// check for filename extension flags
|
||||||
check(t, output, `must_have_one_noun+=("three")`)
|
check(t, output, `must_have_one_noun+=("three")`)
|
||||||
// check for filename extension flags
|
// check for filename extension flags
|
||||||
check(t, output, `flags_completion+=("__handle_filename_extension_flag json|yaml|yml")`)
|
check(t, output, fmt.Sprintf(`flags_completion+=("__%s_handle_filename_extension_flag json|yaml|yml")`, rootCmd.Name()))
|
||||||
// check for custom flags
|
// check for custom flags
|
||||||
check(t, output, `flags_completion+=("__complete_custom")`)
|
check(t, output, `flags_completion+=("__complete_custom")`)
|
||||||
// check for subdirs_in_dir flags
|
// check for subdirs_in_dir flags
|
||||||
check(t, output, `flags_completion+=("__handle_subdirs_in_dir_flag themes")`)
|
check(t, output, fmt.Sprintf(`flags_completion+=("__%s_handle_subdirs_in_dir_flag themes")`, rootCmd.Name()))
|
||||||
|
|
||||||
checkOmit(t, output, deprecatedCmd.Name())
|
checkOmit(t, output, deprecatedCmd.Name())
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue