diff --git a/cmd/completion/main.go b/cmd/completion/main.go index 43e72a0728..355c9644df 100644 --- a/cmd/completion/main.go +++ b/cmd/completion/main.go @@ -14,7 +14,10 @@ import ( func main() { app := cmds.NewApp() app.Commands = []*cli.Command{ - cmds.NewCompletionCommand(completion.Run), + cmds.NewCompletionCommand( + completion.Bash, + completion.Zsh, + ), } if err := app.Run(os.Args); err != nil && !errors.Is(err, context.Canceled) { diff --git a/cmd/k3s/main.go b/cmd/k3s/main.go index 3f243d91fa..deeb131a6a 100644 --- a/cmd/k3s/main.go +++ b/cmd/k3s/main.go @@ -87,7 +87,10 @@ func main() { certCommand, certCommand, ), - cmds.NewCompletionCommand(internalCLIAction(version.Program+"-completion", dataDir, os.Args)), + cmds.NewCompletionCommand( + internalCLIAction(version.Program+"-completion", dataDir, os.Args), + internalCLIAction(version.Program+"-completion", dataDir, os.Args), + ), } if err := app.Run(os.Args); err != nil && !errors.Is(err, context.Canceled) { diff --git a/cmd/server/main.go b/cmd/server/main.go index ed6b1d6665..434b24450f 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -77,7 +77,10 @@ func main() { cert.Rotate, cert.RotateCA, ), - cmds.NewCompletionCommand(completion.Run), + cmds.NewCompletionCommand( + completion.Bash, + completion.Zsh, + ), } if err := app.Run(configfilearg.MustParse(os.Args)); err != nil && !errors.Is(err, context.Canceled) { diff --git a/main.go b/main.go index f000ad6a6e..493c692622 100644 --- a/main.go +++ b/main.go @@ -51,7 +51,10 @@ func main() { cert.Rotate, cert.RotateCA, ), - cmds.NewCompletionCommand(completion.Run), + cmds.NewCompletionCommand( + completion.Bash, + completion.Zsh, + ), } if err := app.Run(configfilearg.MustParse(os.Args)); err != nil && !errors.Is(err, context.Canceled) { diff --git a/pkg/cli/cmds/completion.go b/pkg/cli/cmds/completion.go index 0303282669..a60c0dc3f9 100644 --- a/pkg/cli/cmds/completion.go +++ b/pkg/cli/cmds/completion.go @@ -4,16 +4,34 @@ import ( "github.com/urfave/cli/v2" ) -func NewCompletionCommand(action func(*cli.Context) error) *cli.Command { +func NewCompletionCommand(bash, zsh func(*cli.Context) error) *cli.Command { + installFlag := cli.BoolFlag{ + Name: "i", + Usage: "Install source line to rc file", + } + return &cli.Command{ Name: "completion", Usage: "Install shell completion script", - UsageText: appName + " completion [SHELL] (valid shells: bash, zsh)", - Action: action, - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "i", - Usage: "Install source line to rc file", + UsageText: appName + " completion [COMMAND]", + Subcommands: []*cli.Command{ + { + Name: "bash", + Usage: "Bash completion", + UsageText: appName + " completion bash [OPTIONS]", + Action: bash, + Flags: []cli.Flag{ + &installFlag, + }, + }, + { + Name: "zsh", + Usage: "Zsh completion", + Action: zsh, + UsageText: appName + " completion zsh [OPTIONS]", + Flags: []cli.Flag{ + &installFlag, + }, }, }, } diff --git a/pkg/cli/completion/completion.go b/pkg/cli/completion/completion.go index 3a7e51149c..8967a7bbce 100644 --- a/pkg/cli/completion/completion.go +++ b/pkg/cli/completion/completion.go @@ -9,26 +9,8 @@ import ( "github.com/urfave/cli/v2" ) -func Run(ctx *cli.Context) error { - if ctx.NArg() < 1 { - return fmt.Errorf("must provide a valid SHELL argument") - } - shell := ctx.Args().Get(0) - completetionScript, err := genCompletionScript(shell) - if err != nil { - return err - } - if ctx.Bool("i") { - return writeToRC(shell) - } - fmt.Println(completetionScript) - return nil -} - -func genCompletionScript(shell string) (string, error) { - var completionScript string - if shell == "bash" { - completionScript = fmt.Sprintf(`#!/usr/bin/env bash +var ( + bashScript = `#!/usr/bin/env bash _cli_bash_autocomplete() { if [[ "${COMP_WORDS[0]}" != "source" ]]; then local cur opts base @@ -45,9 +27,9 @@ fi } complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete %s -`, version.Program) - } else if shell == "zsh" { - completionScript = fmt.Sprintf(`#compdef %[1]s +` + + zshScript = `#compdef %[1]s _cli_zsh_autocomplete() { local -a opts @@ -68,36 +50,56 @@ _cli_zsh_autocomplete() { return } -compdef _cli_zsh_autocomplete %[1]s`, version.Program) - } else { - return "", fmt.Errorf("unknown shell: %s", shell) - } +compdef _cli_zsh_autocomplete %[1]s` +) +func Bash(ctx *cli.Context) error { + completetionScript, err := genCompletionScript(bashScript) + if err != nil { + return err + } + if ctx.Bool("i") { + return writeToRC("bash", "/.bashrc") + } + fmt.Println(completetionScript) + return nil +} + +func Zsh(ctx *cli.Context) error { + completetionScript, err := genCompletionScript(zshScript) + if err != nil { + return err + } + if ctx.Bool("i") { + return writeToRC("zsh", "/.zshrc") + } + fmt.Println(completetionScript) + return nil +} + +func genCompletionScript(script string) (string, error) { + completionScript := fmt.Sprintf(script, version.Program) return completionScript, nil } -func writeToRC(shell string) error { - rcFileName := "" - if shell == "bash" { - rcFileName = "/.bashrc" - } else if shell == "zsh" { - rcFileName = "/.zshrc" - } - +func writeToRC(shell, envFileName string) error { home, err := os.UserHomeDir() if err != nil { return nil } - rcFileName = home + rcFileName - f, err := os.OpenFile(rcFileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + + envFileName = home + envFileName + f, err := os.OpenFile(envFileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return err } + defer f.Close() - bashEntry := fmt.Sprintf("# >> %[1]s command completion (start)\n. <(%[1]s completion %s)\n# >> %[1]s command completion (end)", version.Program, shell) - if _, err := f.WriteString(bashEntry); err != nil { + shellEntry := fmt.Sprintf("# >> %[1]s command completion (start)\n. <(%[1]s completion %s)\n# >> %[1]s command completion (end)", version.Program, shell) + if _, err := f.WriteString(shellEntry); err != nil { return err } - fmt.Printf("Autocomplete for %s added to: %s\n", shell, rcFileName) + + fmt.Printf("Autocomplete for %s added to: %s\n", shell, envFileName) return nil }