Skip to content

Commit

Permalink
pass flags root to subcommands, deduplicate
Browse files Browse the repository at this point in the history
  • Loading branch information
XANi committed Jan 25, 2021
1 parent 2e9dc40 commit ba353ea
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 3 deletions.
27 changes: 26 additions & 1 deletion app.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ type App struct {
// single-character bool arguments into one
// i.e. foobar -o -v -> foobar -ov
UseShortOptionHandling bool
// Allow duplicated arguments in subcommands if aliases and description are the same
AllowDuplicateArgs bool
// Relax ordering requirements for arguments inherited from parent subcommands. Implies `AllowDuplicateArgs`
RelaxedArgsOrdering bool

didSetup bool
}
Expand Down Expand Up @@ -181,6 +185,11 @@ func (a *App) Setup() {
if c.HelpName == "" {
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
}
c.AllowDuplicateArgs = a.AllowDuplicateArgs
if c.RelaxedArgsOrdering {
c.RelaxedArgsOrdering = a.RelaxedArgsOrdering
c.Flags = deduplicateFlags(append(a.Flags,c.Flags...))
}
newCommands = append(newCommands, c)
}
a.Commands = newCommands
Expand Down Expand Up @@ -244,6 +253,9 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
}

err = parseIter(set, a, arguments[1:], shellComplete)
if a.AllowDuplicateArgs || a.RelaxedArgsOrdering {
a.Flags = deduplicateFlags(a.Flags)
}
nerr := normalizeFlags(a.Flags, set)
context := NewContext(a, set, &Context{Context: ctx})
if nerr != nil {
Expand Down Expand Up @@ -306,9 +318,15 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) {
}

args := context.Args()

if args.Present() {
name := args.First()
c := a.Command(name)
c.AllowDuplicateArgs = a.AllowDuplicateArgs
if a.RelaxedArgsOrdering {
c.RelaxedArgsOrdering = a.RelaxedArgsOrdering
c.Flags = deduplicateFlags(append(a.Flags,c.Flags...))
}
if c != nil {
return c.Run(context)
}
Expand Down Expand Up @@ -351,13 +369,17 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
newCmds = append(newCmds, c)
}
a.Commands = newCmds

if a.AllowDuplicateArgs || a.RelaxedArgsOrdering {
a.Flags = deduplicateFlags(a.Flags)
}
set, err := a.newFlagSet()
if err != nil {
return err
}

err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete)


nerr := normalizeFlags(a.Flags, set)
context := NewContext(a, set, ctx)

Expand Down Expand Up @@ -446,6 +468,9 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
func (a *App) Command(name string) *Command {
for _, c := range a.Commands {
if c.HasName(name) {
if a.RelaxedArgsOrdering {
c.Flags = deduplicateFlags(append(a.Flags,c.Flags...))
}
return c
}
}
Expand Down
18 changes: 17 additions & 1 deletion command.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ type Command struct {
// single-character bool arguments into one
// i.e. foobar -o -v -> foobar -ov
UseShortOptionHandling bool

// Allow duplicate arguments if they have the same definition
AllowDuplicateArgs bool
// Relax ordering requirements for arguments inherited from parent subcommands. Implies `AllowDuplicateArgs`
RelaxedArgsOrdering bool
// Full name of command for help, defaults to full command name, including parent commands.
HelpName string
commandNamePath []string
Expand Down Expand Up @@ -91,6 +94,13 @@ func (c *Command) FullName() string {
// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
func (c *Command) Run(ctx *Context) (err error) {
if len(c.Subcommands) > 0 {
for idx := range c.Subcommands{
c.Subcommands[idx].AllowDuplicateArgs = c.AllowDuplicateArgs
if c.RelaxedArgsOrdering {
c.Subcommands[idx].RelaxedArgsOrdering = c.RelaxedArgsOrdering
c.Subcommands[idx].Flags = deduplicateFlags(append(c.Flags,c.Subcommands[idx].Flags...))
}
}
return c.startApp(ctx)
}

Expand All @@ -102,6 +112,9 @@ func (c *Command) Run(ctx *Context) (err error) {
if ctx.App.UseShortOptionHandling {
c.UseShortOptionHandling = true
}
if c.AllowDuplicateArgs || c.RelaxedArgsOrdering {
c.Flags = deduplicateFlags(c.Flags)
}

set, err := c.parseFlags(ctx.Args(), ctx.shellComplete)

Expand Down Expand Up @@ -177,6 +190,7 @@ func (c *Command) useShortOptionHandling() bool {
}

func (c *Command) parseFlags(args Args, shellComplete bool) (*flag.FlagSet, error) {

set, err := c.newFlagSet()
if err != nil {
return nil, err
Expand Down Expand Up @@ -247,6 +261,8 @@ func (c *Command) startApp(ctx *Context) error {
app.ErrWriter = ctx.App.ErrWriter
app.ExitErrHandler = ctx.App.ExitErrHandler
app.UseShortOptionHandling = ctx.App.UseShortOptionHandling
app.AllowDuplicateArgs = ctx.App.AllowDuplicateArgs
app.RelaxedArgsOrdering = ctx.App.RelaxedArgsOrdering

app.categories = newCommandCategories()
for _, command := range c.Subcommands {
Expand Down
14 changes: 14 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,20 @@ func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
return nil
}

func deduplicateFlags(flags []Flag) ([]Flag) {
// using list of indexes to skip to preserve order
dedupedFlags := make([]Flag,0)
flagMap := make(map[string]bool)
for _, f := range flags {
if _, ok := flagMap[f.String()]; !ok {
flagMap[f.String()] = true
dedupedFlags = append(dedupedFlags,f)
}
}

return dedupedFlags

}
func makeFlagNameVisitor(names *[]string) func(*flag.Flag) {
return func(f *flag.Flag) {
nameParts := strings.Split(f.Name, ",")
Expand Down
1 change: 0 additions & 1 deletion flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ type DocGenerationFlag interface {

func flagSet(name string, flags []Flag) (*flag.FlagSet, error) {
set := flag.NewFlagSet(name, flag.ContinueOnError)

for _, f := range flags {
if err := f.Apply(set); err != nil {
return nil, err
Expand Down

0 comments on commit ba353ea

Please sign in to comment.