mirror of
https://github.com/89luca89/distrobox.git
synced 2026-08-17 16:34:42 -05:00
feat(completion): add bash and zsh shell completion
Wrappers in completions/ call --generate-shell-completion; a Go ShellComplete callback emits flag names so urfave's default response (subcommands only) is replaced with the full candidate set. This replaces old hard-coded way to auto-complete using shell script's parsing. Signed-off-by: Luca Di Maio <luca.dimaio1@gmail.com>
This commit is contained in:
committed by
Alessio Biancalana
parent
c71075124b
commit
4acf784a7d
@@ -0,0 +1,55 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
// flagCompleter prints the command's subcommand names (with aliases) and
|
||||
// long-form flag names to stdout, one per line. urfave/cli v3's default
|
||||
// shell completion only emits subcommand names; this fills the gap so
|
||||
// `distrobox <cmd> --<TAB>` completes flags too.
|
||||
//
|
||||
// The bash/zsh wrapper scripts filter candidates by the user's current
|
||||
// prefix, so emitting the full set unconditionally is the simplest correct
|
||||
// shape — bash compgen drops anything that doesn't start with `--` when the
|
||||
// user typed `--`, and drops anything that doesn't match the subcommand
|
||||
// prefix otherwise.
|
||||
func flagCompleter(_ context.Context, c *cli.Command) {
|
||||
// VisibleCommands skips help and any Hidden subcommands, matching
|
||||
// what the user would see in `--help` output.
|
||||
for _, sub := range c.VisibleCommands() {
|
||||
fmt.Println(sub.Name)
|
||||
for _, alias := range sub.Aliases {
|
||||
fmt.Println(alias)
|
||||
}
|
||||
}
|
||||
// VisibleFlags skips Hidden flags (e.g. --container-manager,
|
||||
// --sudo-command, --generate-shell-completion).
|
||||
seen := make(map[string]struct{})
|
||||
for _, f := range c.VisibleFlags() {
|
||||
for _, name := range f.Names() {
|
||||
if len(name) <= 1 {
|
||||
continue
|
||||
}
|
||||
if _, dup := seen[name]; dup {
|
||||
continue
|
||||
}
|
||||
seen[name] = struct{}{}
|
||||
fmt.Println("--" + name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// installShellCompleteRecursively walks a command tree and installs
|
||||
// flagCompleter on every node, so nested subcommands (e.g.
|
||||
// `distrobox assemble create`) get the same completion behaviour as
|
||||
// the top-level subcommands.
|
||||
func installShellCompleteRecursively(cmd *cli.Command) {
|
||||
cmd.ShellComplete = flagCompleter
|
||||
for _, sub := range cmd.Commands {
|
||||
installShellCompleteRecursively(sub)
|
||||
}
|
||||
}
|
||||
+14
-4
@@ -22,10 +22,20 @@ type contextKey string
|
||||
const containerManagerKey contextKey = "containerManager"
|
||||
|
||||
func NewRootCommand(cfg *config.Values) *cli.Command {
|
||||
subs := subcommands(cfg)
|
||||
// Install flag-aware completion on every command, including nested
|
||||
// `assemble create`/`assemble rm`. The default would only list
|
||||
// subcommand names; this also emits flag names.
|
||||
for _, sub := range subs {
|
||||
installShellCompleteRecursively(sub)
|
||||
}
|
||||
|
||||
return &cli.Command{
|
||||
Name: "distrobox",
|
||||
Usage: "Use any Linux distribution inside your terminal",
|
||||
Version: version.Version,
|
||||
Name: "distrobox",
|
||||
Usage: "Use any Linux distribution inside your terminal",
|
||||
Version: version.Version,
|
||||
EnableShellCompletion: true,
|
||||
ShellComplete: flagCompleter,
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
@@ -40,7 +50,7 @@ func NewRootCommand(cfg *config.Values) *cli.Command {
|
||||
Value: cfg.SudoProgram,
|
||||
},
|
||||
},
|
||||
Commands: subcommands(cfg),
|
||||
Commands: subs,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user