Skip to content

Commit

Permalink
feat: allow auto-completing context flag (#861)
Browse files Browse the repository at this point in the history
See #853 for more information. Closes #853
  • Loading branch information
phm07 authored Sep 12, 2024
1 parent 42d0c82 commit 7495b14
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 8 deletions.
12 changes: 12 additions & 0 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ func NewRootCommand(s state.State) *cobra.Command {

cmd.PersistentFlags().AddFlagSet(s.Config().FlagSet())

for _, opt := range config.Options {
f := opt.GetFlagCompletionFunc()
if !opt.HasFlags(config.OptionFlagPFlag) || f == nil {
continue
}
// opt.FlagName() is prefixed with --
flagName := opt.FlagName()[2:]
_ = cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return f(s.Client(), s.Config(), cmd, args, toComplete)
})
}

cmd.PersistentPreRunE = func(cmd *cobra.Command, _ []string) error {
var err error
out := os.Stdout
Expand Down
56 changes: 48 additions & 8 deletions internal/state/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import (
"time"

"github.com/spf13/cast"
"github.com/spf13/cobra"
"github.com/spf13/pflag"

"github.com/hetznercloud/cli/internal/cmd/util"
"github.com/hetznercloud/cli/internal/hcapi2"
"github.com/hetznercloud/hcloud-go/v2/hcloud"
)

Expand All @@ -33,13 +35,18 @@ const (
DefaultPreferenceFlags = OptionFlagPreference | OptionFlagConfig | OptionFlagPFlag | OptionFlagEnv
)

type FlagCompletionFunc func(client hcapi2.Client, cfg Config, cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective)

type IOption interface {
// addToFlagSet adds the option to the provided flag set
addToFlagSet(fs *pflag.FlagSet)
// GetName returns the name of the option
GetName() string
// GetDescription returns the description of the option
GetDescription() string
// GetFlagCompletionFunc returns the completion function for this option's flag.
// If it doesn't exist it returns nil.
GetFlagCompletionFunc() FlagCompletionFunc
// ConfigKey returns the key used in the config file. If the option is not configurable via the config file, an empty string is returned
ConfigKey() string
// EnvVar returns the name of the environment variable. If the option is not configurable via an environment variable, an empty string is returned
Expand Down Expand Up @@ -80,6 +87,7 @@ var (
DefaultConfigPath(),
OptionFlagPFlag|OptionFlagEnv,
nil,
nil,
)

OptionToken = newOpt(
Expand All @@ -88,13 +96,22 @@ var (
"",
OptionFlagConfig|OptionFlagEnv|OptionFlagSensitive,
nil,
nil,
)

OptionContext = newOpt(
"context",
"Currently active context",
"",
OptionFlagConfig|OptionFlagEnv|OptionFlagPFlag,
func(_ hcapi2.Client, cfg Config, _ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
ctxs := cfg.Contexts()
ctxNames := make([]string, 0, len(ctxs))
for _, ctx := range ctxs {
ctxNames = append(ctxNames, ctx.Name())
}
return ctxNames, cobra.ShellCompDirectiveDefault
},
&overrides{configKey: "active_context"},
)

Expand All @@ -104,6 +121,7 @@ var (
hcloud.Endpoint,
DefaultPreferenceFlags,
nil,
nil,
)

OptionDebug = newOpt(
Expand All @@ -112,6 +130,7 @@ var (
false,
DefaultPreferenceFlags,
nil,
nil,
)

OptionDebugFile = newOpt(
Expand All @@ -120,6 +139,7 @@ var (
"",
DefaultPreferenceFlags,
nil,
nil,
)

OptionPollInterval = newOpt(
Expand All @@ -128,6 +148,7 @@ var (
500*time.Millisecond,
DefaultPreferenceFlags,
nil,
nil,
)

OptionQuiet = newOpt(
Expand All @@ -136,6 +157,7 @@ var (
false,
DefaultPreferenceFlags,
nil,
nil,
)

OptionDefaultSSHKeys = newOpt(
Expand All @@ -144,6 +166,7 @@ var (
[]string{},
(DefaultPreferenceFlags&^OptionFlagPFlag)|OptionFlagSlice,
nil,
nil,
)

OptionSortCertificate = newOpt(
Expand All @@ -152,6 +175,7 @@ var (
[]string{"id:asc"},
(DefaultPreferenceFlags&^OptionFlagPFlag)|OptionFlagSlice|OptionFlagHidden,
nil,
nil,
)

OptionSortDatacenter = newOpt(
Expand All @@ -160,6 +184,7 @@ var (
[]string{"id:asc"},
(DefaultPreferenceFlags&^OptionFlagPFlag)|OptionFlagSlice|OptionFlagHidden,
nil,
nil,
)

OptionSortFirewall = newOpt(
Expand All @@ -168,6 +193,7 @@ var (
[]string{"id:asc"},
(DefaultPreferenceFlags&^OptionFlagPFlag)|OptionFlagSlice|OptionFlagHidden,
nil,
nil,
)

OptionSortFloatingIP = newOpt(
Expand All @@ -176,6 +202,7 @@ var (
[]string{"id:asc"},
(DefaultPreferenceFlags&^OptionFlagPFlag)|OptionFlagSlice|OptionFlagHidden,
nil,
nil,
)

OptionSortImage = newOpt(
Expand All @@ -184,6 +211,7 @@ var (
[]string{"id:asc"},
(DefaultPreferenceFlags&^OptionFlagPFlag)|OptionFlagSlice|OptionFlagHidden,
nil,
nil,
)

OptionSortLoadBalancer = newOpt(
Expand All @@ -192,6 +220,7 @@ var (
[]string{"id:asc"},
(DefaultPreferenceFlags&^OptionFlagPFlag)|OptionFlagSlice|OptionFlagHidden,
nil,
nil,
)

OptionSortLocation = newOpt(
Expand All @@ -200,6 +229,7 @@ var (
[]string{"id:asc"},
(DefaultPreferenceFlags&^OptionFlagPFlag)|OptionFlagSlice|OptionFlagHidden,
nil,
nil,
)

OptionSortPlacementGroup = newOpt(
Expand All @@ -208,6 +238,7 @@ var (
[]string{"id:asc"},
(DefaultPreferenceFlags&^OptionFlagPFlag)|OptionFlagSlice|OptionFlagHidden,
nil,
nil,
)

OptionSortPrimaryIP = newOpt(
Expand All @@ -216,6 +247,7 @@ var (
[]string{"id:asc"},
(DefaultPreferenceFlags&^OptionFlagPFlag)|OptionFlagSlice|OptionFlagHidden,
nil,
nil,
)

OptionSortServer = newOpt(
Expand All @@ -224,6 +256,7 @@ var (
[]string{"id:asc"},
(DefaultPreferenceFlags&^OptionFlagPFlag)|OptionFlagSlice|OptionFlagHidden,
nil,
nil,
)

OptionSortSSHKey = newOpt(
Expand All @@ -232,6 +265,7 @@ var (
[]string{"id:asc"},
(DefaultPreferenceFlags&^OptionFlagPFlag)|OptionFlagSlice|OptionFlagHidden,
nil,
nil,
)

OptionSortVolume = newOpt(
Expand All @@ -240,15 +274,17 @@ var (
[]string{"id:asc"},
(DefaultPreferenceFlags&^OptionFlagPFlag)|OptionFlagSlice|OptionFlagHidden,
nil,
nil,
)
)

type Option[T any] struct {
Name string
Description string
Default T
Flags OptionFlag
overrides *overrides
Name string
Description string
Default T
Flags OptionFlag
FlagCompletionFunc FlagCompletionFunc
overrides *overrides
}

func (o *Option[T]) Get(c Config) (T, error) {
Expand Down Expand Up @@ -322,6 +358,10 @@ func (o *Option[T]) GetDescription() string {
return o.Description
}

func (o *Option[T]) GetFlagCompletionFunc() FlagCompletionFunc {
return o.FlagCompletionFunc
}

func (o *Option[T]) ConfigKey() string {
if !o.HasFlags(OptionFlagConfig) {
return ""
Expand Down Expand Up @@ -423,15 +463,15 @@ func (o *Option[T]) addToFlagSet(fs *pflag.FlagSet) {
}
}

func newOpt[T any](name, description string, def T, flags OptionFlag, ov *overrides) *Option[T] {
o := &Option[T]{Name: name, Description: description, Default: def, Flags: flags, overrides: ov}
func newOpt[T any](name, description string, def T, flags OptionFlag, f FlagCompletionFunc, ov *overrides) *Option[T] {
o := &Option[T]{Name: name, Description: description, Default: def, Flags: flags, FlagCompletionFunc: f, overrides: ov}
Options[name] = o
return o
}

// NewTestOption is a helper function to create an option for testing purposes
func NewTestOption[T any](name, description string, def T, flags OptionFlag, ov *overrides) (*Option[T], func()) {
opt := newOpt(name, description, def, flags, ov)
opt := newOpt(name, description, def, flags, nil, ov)
return opt, func() {
delete(Options, name)
}
Expand Down

0 comments on commit 7495b14

Please sign in to comment.