package cli import ( "flag" "os" "path/filepath" "sort" "strings" ) func (cmd *Command) setupDefaults(osArgs []string) { if cmd.didSetupDefaults { tracef("already did setup (cmd=%[1]q)", cmd.Name) return } cmd.didSetupDefaults = true isRoot := cmd.parent == nil tracef("isRoot? %[1]v (cmd=%[2]q)", isRoot, cmd.Name) if cmd.ShellComplete == nil { tracef("setting default ShellComplete (cmd=%[1]q)", cmd.Name) cmd.ShellComplete = DefaultCompleteWithFlags } if cmd.Name == "" && isRoot { name := filepath.Base(osArgs[0]) tracef("setting cmd.Name from first arg basename (cmd=%[1]q)", name) cmd.Name = name } if cmd.Usage == "" && isRoot { tracef("setting default Usage (cmd=%[1]q)", cmd.Name) cmd.Usage = "A new cli application" } if cmd.Version == "" { tracef("setting HideVersion=true due to empty Version (cmd=%[1]q)", cmd.Name) cmd.HideVersion = true } if cmd.Action == nil { tracef("setting default Action as help command action (cmd=%[1]q)", cmd.Name) cmd.Action = helpCommandAction } if cmd.Reader == nil { if cmd.parent != nil && cmd.parent.Reader != nil { tracef("inheriting Reader from parent (cmd=%[1]q)", cmd.Name) cmd.Reader = cmd.parent.Reader } else { tracef("setting default Reader as os.Stdin (cmd=%[1]q)", cmd.Name) cmd.Reader = os.Stdin } } if cmd.Writer == nil { if cmd.parent != nil && cmd.parent.Writer != nil { tracef("inheriting Writer from parent (cmd=%[1]q)", cmd.Name) cmd.Writer = cmd.parent.Writer } else { tracef("setting default Writer as os.Stdout (cmd=%[1]q)", cmd.Name) cmd.Writer = os.Stdout } } if cmd.ErrWriter == nil { if cmd.parent != nil && cmd.parent.ErrWriter != nil { tracef("inheriting ErrWriter from parent (cmd=%[1]q)", cmd.Name) cmd.ErrWriter = cmd.parent.ErrWriter } else { tracef("setting default ErrWriter as os.Stderr (cmd=%[1]q)", cmd.Name) cmd.ErrWriter = os.Stderr } } if cmd.AllowExtFlags { tracef("visiting all flags given AllowExtFlags=true (cmd=%[1]q)", cmd.Name) // add global flags added by other packages flag.VisitAll(func(f *flag.Flag) { // skip test flags if !strings.HasPrefix(f.Name, ignoreFlagPrefix) { cmd.Flags = append(cmd.Flags, &extFlag{f}) } }) } for _, subCmd := range cmd.Commands { tracef("setting sub-command (cmd=%[1]q) parent as self (cmd=%[2]q)", subCmd.Name, cmd.Name) subCmd.parent = cmd } cmd.ensureHelp() if !cmd.HideVersion && isRoot { tracef("appending version flag (cmd=%[1]q)", cmd.Name) if !cmd.globaVersionFlagAdded { var localVersionFlag Flag if globalVersionFlag, ok := VersionFlag.(*BoolFlag); ok { flag := *globalVersionFlag // Drop any alias a user flag already claims (e.g. -v // for --verbose) so the user flag wins but --version // still works. See #2229. flag.Aliases = dropClashingAliases(flag.Aliases, cmd.allFlags(), flag.Name) localVersionFlag = &flag } else { localVersionFlag = VersionFlag } if !flagNamesInUse(cmd.allFlags(), localVersionFlag.Names()) { cmd.appendFlag(localVersionFlag) cmd.versionFlag = localVersionFlag cmd.globaVersionFlagAdded = true } } } if cmd.PrefixMatchCommands && cmd.SuggestCommandFunc == nil { tracef("setting default SuggestCommandFunc (cmd=%[1]q)", cmd.Name) cmd.SuggestCommandFunc = suggestCommand } if isRoot && cmd.EnableShellCompletion || cmd.ConfigureShellCompletionCommand != nil { completionCommand := buildCompletionCommand(cmd.Name) if cmd.ShellCompletionCommandName != "" { tracef( "setting completion command name (%[1]q) from "+ "cmd.ShellCompletionCommandName (cmd=%[2]q)", cmd.ShellCompletionCommandName, cmd.Name, ) completionCommand.Name = cmd.ShellCompletionCommandName } tracef("appending completionCommand (cmd=%[1]q)", cmd.Name) cmd.appendCommand(completionCommand) if cmd.ConfigureShellCompletionCommand != nil { cmd.ConfigureShellCompletionCommand(completionCommand) } } tracef("setting command categories (cmd=%[1]q)", cmd.Name) cmd.categories = newCommandCategories() for _, subCmd := range cmd.Commands { cmd.categories.AddCommand(subCmd.Category, subCmd) } tracef("sorting command categories (cmd=%[1]q)", cmd.Name) sort.Sort(cmd.categories.(*commandCategories)) tracef("setting category on mutually exclusive flags (cmd=%[1]q)", cmd.Name) for _, grp := range cmd.MutuallyExclusiveFlags { grp.propagateCategory() } tracef("setting flag categories (cmd=%[1]q)", cmd.Name) cmd.flagCategories = newFlagCategoriesFromFlags(cmd.allFlags()) if cmd.Metadata == nil { tracef("setting default Metadata (cmd=%[1]q)", cmd.Name) cmd.Metadata = map[string]any{} } cmd.setFlags = map[Flag]struct{}{} } func (cmd *Command) setupCommandGraph() { tracef("setting up command graph (cmd=%[1]q)", cmd.Name) _ = cmd.Walk(func(sub *Command) error { for _, subCmd := range sub.Commands { subCmd.parent = sub subCmd.setupSubcommand() } return nil }) } func (cmd *Command) setupSubcommand() { tracef("setting up self as sub-command (cmd=%[1]q)", cmd.Name) cmd.ensureHelp() tracef("setting command categories (cmd=%[1]q)", cmd.Name) cmd.categories = newCommandCategories() for _, subCmd := range cmd.Commands { cmd.categories.AddCommand(subCmd.Category, subCmd) } tracef("sorting command categories (cmd=%[1]q)", cmd.Name) sort.Sort(cmd.categories.(*commandCategories)) tracef("setting category on mutually exclusive flags (cmd=%[1]q)", cmd.Name) for _, grp := range cmd.MutuallyExclusiveFlags { grp.propagateCategory() } tracef("setting flag categories (cmd=%[1]q)", cmd.Name) cmd.flagCategories = newFlagCategoriesFromFlags(cmd.allFlags()) } func flagNamesInUse(flags []Flag, names []string) bool { for _, name := range names { for _, fl := range flags { for _, flagName := range fl.Names() { if flagName == name { return true } } } } return false } func (cmd *Command) hideHelp() bool { tracef("hide help (cmd=%[1]q)", cmd.Name) for c := cmd; c != nil; c = c.parent { if c.HideHelp { return true } } return false } func (cmd *Command) ensureHelp() { tracef("ensuring help (cmd=%[1]q)", cmd.Name) helpCommand := buildHelpCommand(true) if !cmd.hideHelp() { if cmd.Command(helpCommand.Name) == nil { if !cmd.HideHelpCommand { tracef("appending helpCommand (cmd=%[1]q)", cmd.Name) cmd.appendCommand(helpCommand) } } if HelpFlag != nil { if !cmd.globaHelpFlagAdded { var localHelpFlag Flag if globalHelpFlag, ok := HelpFlag.(*BoolFlag); ok { flag := *globalHelpFlag localHelpFlag = &flag } else { localHelpFlag = HelpFlag } tracef("appending HelpFlag (cmd=%[1]q)", cmd.Name) cmd.appendFlag(localHelpFlag) cmd.globaHelpFlagAdded = true } else { tracef("HelpFlag already added, skip (cmd=%[1]q)", cmd.Name) } } } } // dropClashingAliases removes aliases from `aliases` that are already // claimed by a flag in `userFlags` (either as a primary name or as one // of its own aliases). Aliases equal to `selfName` are kept so the // flag's primary name doesn't accidentally remove itself. func dropClashingAliases(aliases []string, userFlags []Flag, selfName string) []string { if len(aliases) == 0 || len(userFlags) == 0 { return aliases } taken := map[string]struct{}{} for _, f := range userFlags { for _, n := range f.Names() { taken[n] = struct{}{} } } kept := aliases[:0:0] for _, a := range aliases { if a == selfName { kept = append(kept, a) continue } if _, ok := taken[a]; ok { continue } kept = append(kept, a) } return kept }