commit
bfc3e4e43d
5 changed files with 30 additions and 178 deletions
|
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/cli/cli/internal/cobrafish"
|
||||
"github.com/cli/cli/utils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
|
@ -58,7 +57,7 @@ Homebrew, see <https://docs.brew.sh/Shell-Completion>
|
|||
case "powershell":
|
||||
return RootCmd.GenPowerShellCompletion(cmd.OutOrStdout())
|
||||
case "fish":
|
||||
return cobrafish.GenCompletion(RootCmd, cmd.OutOrStdout())
|
||||
return RootCmd.GenFishCompletion(cmd.OutOrStdout(), true)
|
||||
default:
|
||||
return fmt.Errorf("unsupported shell type %q", shellType)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -336,9 +336,33 @@ func determineBaseRepo(apiClient *api.Client, cmd *cobra.Command, ctx context.Co
|
|||
return baseRepo, nil
|
||||
}
|
||||
|
||||
func rootHelpFunc(command *cobra.Command, s []string) {
|
||||
func rootHelpFunc(command *cobra.Command, args []string) {
|
||||
if command != RootCmd {
|
||||
cobraDefaultHelpFunc(command, s)
|
||||
// Display helpful error message in case subcommand name was mistyped.
|
||||
// This matches Cobra's behavior for root command, which Cobra
|
||||
// confusingly doesn't apply to nested commands.
|
||||
if command.Parent() == RootCmd && len(args) >= 2 {
|
||||
if command.SuggestionsMinimumDistance <= 0 {
|
||||
command.SuggestionsMinimumDistance = 2
|
||||
}
|
||||
candidates := command.SuggestionsFor(args[1])
|
||||
|
||||
errOut := command.OutOrStderr()
|
||||
fmt.Fprintf(errOut, "unknown command %q for %q\n", args[1], "gh "+args[0])
|
||||
|
||||
if len(candidates) > 0 {
|
||||
fmt.Fprint(errOut, "\nDid you mean this?\n")
|
||||
for _, c := range candidates {
|
||||
fmt.Fprintf(errOut, "\t%s\n", c)
|
||||
}
|
||||
fmt.Fprint(errOut, "\n")
|
||||
}
|
||||
|
||||
oldOut := command.OutOrStdout()
|
||||
command.SetOut(errOut)
|
||||
defer command.SetOut(oldOut)
|
||||
}
|
||||
cobraDefaultHelpFunc(command, args)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -19,7 +19,7 @@ require (
|
|||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/shurcooL/githubv4 v0.0.0-20191127044304-8f68eb5628d0
|
||||
github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f // indirect
|
||||
github.com/spf13/cobra v0.0.6
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.5.1
|
||||
golang.org/x/crypto v0.0.0-20200219234226-1ad67e1f0ef4
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -158,8 +158,8 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
|
|||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs=
|
||||
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
|
|
|
|||
|
|
@ -1,171 +0,0 @@
|
|||
// imported from https://github.com/spf13/cobra/pull/754
|
||||
// author: Tim Reddehase
|
||||
|
||||
package cobrafish
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func GenCompletion(c *cobra.Command, w io.Writer) error {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
writeFishPreamble(c, buf)
|
||||
writeFishCommandCompletion(c, c, buf)
|
||||
|
||||
_, err := buf.WriteTo(w)
|
||||
return err
|
||||
}
|
||||
|
||||
func writeFishPreamble(cmd *cobra.Command, buf *bytes.Buffer) {
|
||||
subCommandNames := []string{}
|
||||
rangeCommands(cmd, func(subCmd *cobra.Command) {
|
||||
subCommandNames = append(subCommandNames, subCmd.Name())
|
||||
})
|
||||
buf.WriteString(fmt.Sprintf(`
|
||||
function __fish_%s_no_subcommand --description 'Test if %s has yet to be given the subcommand'
|
||||
for i in (commandline -opc)
|
||||
if contains -- $i %s
|
||||
return 1
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
function __fish_%s_seen_subcommand_path --description 'Test whether the full path of subcommands is the current path'
|
||||
set -l cmd (commandline -opc)
|
||||
set -e cmd[1]
|
||||
set -l pattern (string replace -a " " ".+" "$argv")
|
||||
string match -r "$pattern" (string trim -- "$cmd")
|
||||
end
|
||||
# borrowed from current fish-shell master, since it is not in current 2.7.1 release
|
||||
function __fish_seen_argument
|
||||
argparse 's/short=+' 'l/long=+' -- $argv
|
||||
set cmd (commandline -co)
|
||||
set -e cmd[1]
|
||||
for t in $cmd
|
||||
for s in $_flag_s
|
||||
if string match -qr "^-[A-z0-9]*"$s"[A-z0-9]*\$" -- $t
|
||||
return 0
|
||||
end
|
||||
end
|
||||
for l in $_flag_l
|
||||
if string match -q -- "--$l" $t
|
||||
return 0
|
||||
end
|
||||
end
|
||||
end
|
||||
return 1
|
||||
end
|
||||
`, cmd.Name(), cmd.Name(), strings.Join(subCommandNames, " "), cmd.Name()))
|
||||
}
|
||||
|
||||
func writeFishCommandCompletion(rootCmd, cmd *cobra.Command, buf *bytes.Buffer) {
|
||||
rangeCommands(cmd, func(subCmd *cobra.Command) {
|
||||
condition := commandCompletionCondition(rootCmd, cmd)
|
||||
escapedDescription := strings.Replace(subCmd.Short, "'", "\\'", -1)
|
||||
buf.WriteString(fmt.Sprintf("complete -c %s -f %s -a %s -d '%s'\n", rootCmd.Name(), condition, subCmd.Name(), escapedDescription))
|
||||
})
|
||||
for _, validArg := range append(cmd.ValidArgs, cmd.ArgAliases...) {
|
||||
condition := commandCompletionCondition(rootCmd, cmd)
|
||||
buf.WriteString(
|
||||
fmt.Sprintf("complete -c %s -f %s -a %s -d '%s'\n",
|
||||
rootCmd.Name(), condition, validArg, fmt.Sprintf("Positional Argument to %s", cmd.Name())))
|
||||
}
|
||||
writeCommandFlagsCompletion(rootCmd, cmd, buf)
|
||||
rangeCommands(cmd, func(subCmd *cobra.Command) {
|
||||
writeFishCommandCompletion(rootCmd, subCmd, buf)
|
||||
})
|
||||
}
|
||||
|
||||
func writeCommandFlagsCompletion(rootCmd, cmd *cobra.Command, buf *bytes.Buffer) {
|
||||
cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
|
||||
if nonCompletableFlag(flag) {
|
||||
return
|
||||
}
|
||||
writeCommandFlagCompletion(rootCmd, cmd, buf, flag)
|
||||
})
|
||||
cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {
|
||||
if nonCompletableFlag(flag) {
|
||||
return
|
||||
}
|
||||
writeCommandFlagCompletion(rootCmd, cmd, buf, flag)
|
||||
})
|
||||
}
|
||||
|
||||
func writeCommandFlagCompletion(rootCmd, cmd *cobra.Command, buf *bytes.Buffer, flag *pflag.Flag) {
|
||||
shortHandPortion := ""
|
||||
if len(flag.Shorthand) > 0 {
|
||||
shortHandPortion = fmt.Sprintf("-s %s", flag.Shorthand)
|
||||
}
|
||||
condition := completionCondition(rootCmd, cmd)
|
||||
escapedUsage := strings.Replace(flag.Usage, "'", "\\'", -1)
|
||||
buf.WriteString(fmt.Sprintf("complete -c %s -f %s %s %s -l %s -d '%s'\n",
|
||||
rootCmd.Name(), condition, flagRequiresArgumentCompletion(flag), shortHandPortion, flag.Name, escapedUsage))
|
||||
}
|
||||
|
||||
func flagRequiresArgumentCompletion(flag *pflag.Flag) string {
|
||||
if flag.Value.Type() != "bool" {
|
||||
return "-r"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func subCommandPath(rootCmd *cobra.Command, cmd *cobra.Command) string {
|
||||
path := make([]string, 0, 1)
|
||||
currentCmd := cmd
|
||||
if rootCmd == cmd {
|
||||
return ""
|
||||
}
|
||||
for {
|
||||
path = append([]string{currentCmd.Name()}, path...)
|
||||
if currentCmd.Parent() == rootCmd {
|
||||
return strings.Join(path, " ")
|
||||
}
|
||||
currentCmd = currentCmd.Parent()
|
||||
}
|
||||
}
|
||||
|
||||
func rangeCommands(cmd *cobra.Command, callback func(subCmd *cobra.Command)) {
|
||||
for _, subCmd := range cmd.Commands() {
|
||||
if !subCmd.IsAvailableCommand() || strings.HasPrefix(subCmd.Use, "help") {
|
||||
continue
|
||||
}
|
||||
callback(subCmd)
|
||||
}
|
||||
}
|
||||
|
||||
func commandCompletionCondition(rootCmd, cmd *cobra.Command) string {
|
||||
localNonPersistentFlags := cmd.LocalNonPersistentFlags()
|
||||
bareConditions := make([]string, 0, 1)
|
||||
if rootCmd != cmd {
|
||||
bareConditions = append(bareConditions, fmt.Sprintf("__fish_%s_seen_subcommand_path %s", rootCmd.Name(), subCommandPath(rootCmd, cmd)))
|
||||
} else {
|
||||
bareConditions = append(bareConditions, fmt.Sprintf("__fish_%s_no_subcommand", rootCmd.Name()))
|
||||
}
|
||||
localNonPersistentFlags.VisitAll(func(flag *pflag.Flag) {
|
||||
flagSelector := fmt.Sprintf("-l %s", flag.Name)
|
||||
if len(flag.Shorthand) > 0 {
|
||||
flagSelector = fmt.Sprintf("-s %s %s", flag.Shorthand, flagSelector)
|
||||
}
|
||||
bareConditions = append(bareConditions, fmt.Sprintf("not __fish_seen_argument %s", flagSelector))
|
||||
})
|
||||
return fmt.Sprintf("-n '%s'", strings.Join(bareConditions, "; and "))
|
||||
}
|
||||
|
||||
func completionCondition(rootCmd, cmd *cobra.Command) string {
|
||||
condition := fmt.Sprintf("-n '__fish_%s_no_subcommand'", rootCmd.Name())
|
||||
if rootCmd != cmd {
|
||||
condition = fmt.Sprintf("-n '__fish_%s_seen_subcommand_path %s'", rootCmd.Name(), subCommandPath(rootCmd, cmd))
|
||||
}
|
||||
return condition
|
||||
}
|
||||
|
||||
func nonCompletableFlag(flag *pflag.Flag) bool {
|
||||
return flag.Hidden || len(flag.Deprecated) > 0
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue