diff --git a/cmd/completion/completion.go b/cmd/completion/completion.go new file mode 100644 index 0000000..0a65b2b --- /dev/null +++ b/cmd/completion/completion.go @@ -0,0 +1,75 @@ +package completion + +import ( + "os" + + "github.com/fosrl/cli/internal/logger" + "github.com/spf13/cobra" +) + +func CompletionCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "completion [bash|zsh|fish]", + Short: "Generate shell completion script", + Long: `Generate shell completion script for the specified shell. + +The completion script can be sourced to enable command-line completion for pangolin. + +Bash: + $ source <(pangolin completion bash) + + To load completions for each session, execute once: + Linux: + $ pangolin completion bash > /etc/bash_completion.d/pangolin + macOS: + $ pangolin completion bash > /usr/local/etc/bash_completion.d/pangolin + +Zsh: + If shell completion is not already enabled in your environment, you will need + to enable it. You can execute the following once: + $ echo "autoload -U compinit; compinit" >> ~/.zshrc + + To load completions for each session, execute once: + $ pangolin completion zsh > "${fpath[1]}/_pangolin" + + You will need to start a new shell for this setup to take effect. + +Fish: + $ pangolin completion fish | source + + To load completions for each session, execute once: + $ pangolin completion fish > ~/.config/fish/completions/pangolin.fish +`, + DisableFlagsInUseLine: true, + ValidArgs: []string{"bash", "zsh", "fish"}, + Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), + Run: func(cmd *cobra.Command, args []string) { + if err := completionMain(cmd, args); err != nil { + os.Exit(1) + } + }, + } + + return cmd +} + +func completionMain(cmd *cobra.Command, args []string) error { + switch args[0] { + case "bash": + if err := cmd.Root().GenBashCompletion(os.Stdout); err != nil { + logger.Error("Failed to generate bash completion: %v", err) + return err + } + case "zsh": + if err := cmd.Root().GenZshCompletion(os.Stdout); err != nil { + logger.Error("Failed to generate zsh completion: %v", err) + return err + } + case "fish": + if err := cmd.Root().GenFishCompletion(os.Stdout, true); err != nil { + logger.Error("Failed to generate fish completion: %v", err) + return err + } + } + return nil +} diff --git a/cmd/root.go b/cmd/root.go index 31f534b..e43e727 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -9,6 +9,7 @@ import ( "github.com/fosrl/cli/cmd/auth" "github.com/fosrl/cli/cmd/auth/login" "github.com/fosrl/cli/cmd/auth/logout" + "github.com/fosrl/cli/cmd/completion" "github.com/fosrl/cli/cmd/down" "github.com/fosrl/cli/cmd/logs" selectcmd "github.com/fosrl/cli/cmd/select" @@ -31,16 +32,14 @@ import ( // state when doing doc generation. func RootCommand(initResources bool) (*cobra.Command, error) { cmd := &cobra.Command{ - Use: "pangolin", - Short: "Pangolin CLI", - SilenceUsage: true, - CompletionOptions: cobra.CompletionOptions{ - HiddenDefaultCmd: true, - }, + Use: "pangolin", + Short: "Pangolin CLI", + SilenceUsage: true, PersistentPreRunE: mainCommandPreRun, } cmd.AddCommand(auth.AuthCommand()) + cmd.AddCommand(completion.CompletionCmd()) cmd.AddCommand(selectcmd.SelectCmd()) cmd.AddCommand(up.UpCmd()) cmd.AddCommand(down.DownCmd()) @@ -98,10 +97,10 @@ func RootCommand(initResources bool) (*cobra.Command, error) { func mainCommandPreRun(cmd *cobra.Command, args []string) error { cfg := config.ConfigFromContext(cmd.Context()) - // Skip init/update check for version and update commands + // Skip init/update check for version, update, and completion commands // Check both the command name and if it's one of these specific commands cmdName := cmd.Name() - if cmdName == "version" || cmdName == "update" { + if cmdName == "version" || cmdName == "update" || cmdName == "completion" { return nil } diff --git a/docs/pangolin.md b/docs/pangolin.md index 0b3402a..7aa7dda 100644 --- a/docs/pangolin.md +++ b/docs/pangolin.md @@ -11,6 +11,7 @@ Pangolin CLI ### SEE ALSO * [pangolin auth](pangolin_auth.md) - Authentication commands +* [pangolin completion](pangolin_completion.md) - Generate shell completion script * [pangolin down](pangolin_down.md) - Stop a connection * [pangolin login](pangolin_login.md) - Login to Pangolin * [pangolin logout](pangolin_logout.md) - Logout from Pangolin diff --git a/docs/pangolin_completion.md b/docs/pangolin_completion.md new file mode 100644 index 0000000..1159cf9 --- /dev/null +++ b/docs/pangolin_completion.md @@ -0,0 +1,50 @@ +## pangolin completion + +Generate shell completion script + +### Synopsis + +Generate shell completion script for the specified shell. + +The completion script can be sourced to enable command-line completion for pangolin. + +Bash: + $ source <(pangolin completion bash) + + To load completions for each session, execute once: + Linux: + $ pangolin completion bash > /etc/bash_completion.d/pangolin + macOS: + $ pangolin completion bash > /usr/local/etc/bash_completion.d/pangolin + +Zsh: + If shell completion is not already enabled in your environment, you will need + to enable it. You can execute the following once: + $ echo "autoload -U compinit; compinit" >> ~/.zshrc + + To load completions for each session, execute once: + $ pangolin completion zsh > "${fpath[1]}/_pangolin" + + You will need to start a new shell for this setup to take effect. + +Fish: + $ pangolin completion fish | source + + To load completions for each session, execute once: + $ pangolin completion fish > ~/.config/fish/completions/pangolin.fish + + +``` +pangolin completion [bash|zsh|fish] +``` + +### Options + +``` + -h, --help help for completion +``` + +### SEE ALSO + +* [pangolin](pangolin.md) - Pangolin CLI + diff --git a/go.mod b/go.mod index 4635ea0..0e7ef68 100644 --- a/go.mod +++ b/go.mod @@ -7,11 +7,10 @@ require ( github.com/charmbracelet/bubbletea v1.3.6 github.com/charmbracelet/huh v0.8.0 github.com/charmbracelet/lipgloss v1.1.0 - github.com/fosrl/newt v0.0.0 + github.com/fosrl/newt v1.8.0 github.com/fosrl/olm v0.0.0 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/spf13/cobra v1.10.1 - github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 ) @@ -51,6 +50,7 @@ require ( github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/vishvananda/netlink v1.3.1 // indirect github.com/vishvananda/netns v0.0.5 // indirect @@ -76,4 +76,4 @@ require ( replace github.com/fosrl/olm v0.0.0 => ../olm -replace github.com/fosrl/newt v0.0.0 => ../newt +replace github.com/fosrl/newt v1.8.0 => ../newt diff --git a/go.sum b/go.sum index b4118da..6738a8b 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/fosrl/newt v1.8.0 h1:wIRCO2shhCpkFzsbNbb4g2LC7mPzIpp2ialNveBMJy4= +github.com/fosrl/newt v1.8.0/go.mod h1:pol958CEs0nQmo/35Ltv0CGksheIKCS2hoNvdTVLEcI= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= @@ -60,8 +62,8 @@ github.com/godbus/dbus/v5 v5.2.0 h1:3WexO+U+yg9T70v9FdHr9kCxYlazaAXUhx2VMkbfax8= github.com/godbus/dbus/v5 v5.2.0/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=