diff --git a/app.go b/app.go index 10198f4332..9512392907 100644 --- a/app.go +++ b/app.go @@ -250,14 +250,7 @@ func (a *App) Setup() { } sort.Sort(a.categories.(*commandCategories)) - a.flagCategories = newFlagCategories() - for _, fl := range a.Flags { - if cf, ok := fl.(CategorizableFlag); ok { - if cf.GetCategory() != "" { - a.flagCategories.AddFlag(cf.GetCategory(), cf) - } - } - } + a.flagCategories = newFlagCategoriesFromFlags(a.Flags) if a.Metadata == nil { a.Metadata = make(map[string]interface{}) diff --git a/app_test.go b/app_test.go index 7f2a2144e5..d2cbb677cf 100644 --- a/app_test.go +++ b/app_test.go @@ -2303,37 +2303,6 @@ func TestApp_VisibleCategories(t *testing.T) { expect(t, []CommandCategory{}, app.VisibleCategories()) } -func TestApp_VisibleFlagCategories(t *testing.T) { - app := &App{ - Flags: []Flag{ - &StringFlag{ - Name: "strd", // no category set - }, - &Int64Flag{ - Name: "intd", - Aliases: []string{"altd1", "altd2"}, - Category: "cat1", - }, - }, - } - app.Setup() - vfc := app.VisibleFlagCategories() - if len(vfc) != 1 { - t.Fatalf("unexpected visible flag categories %+v", vfc) - } - if vfc[0].Name() != "cat1" { - t.Errorf("expected category name cat1 got %s", vfc[0].Name()) - } - if len(vfc[0].Flags()) != 1 { - t.Fatalf("expected flag category to have just one flag got %+v", vfc[0].Flags()) - } - - fl := vfc[0].Flags()[0] - if !reflect.DeepEqual(fl.Names(), []string{"intd", "altd1", "altd2"}) { - t.Errorf("unexpected flag %+v", fl.Names()) - } -} - func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { app := &App{ Action: func(c *Context) error { return nil }, diff --git a/category.go b/category.go index 7aca0c7684..ccc043c254 100644 --- a/category.go +++ b/category.go @@ -100,10 +100,23 @@ func newFlagCategories() FlagCategories { func newFlagCategoriesFromFlags(fs []Flag) FlagCategories { fc := newFlagCategories() + + var categorized bool for _, fl := range fs { if cf, ok := fl.(CategorizableFlag); ok { - if cf.GetCategory() != "" { - fc.AddFlag(cf.GetCategory(), cf) + if cat := cf.GetCategory(); cat != "" { + fc.AddFlag(cat, cf) + categorized = true + } + } + } + + if categorized == true { + for _, fl := range fs { + if cf, ok := fl.(CategorizableFlag); ok { + if cf.GetCategory() == "" { + fc.AddFlag("", fl) + } } } } diff --git a/command_test.go b/command_test.go index 478c3251a6..70caee750d 100644 --- a/command_test.go +++ b/command_test.go @@ -470,17 +470,17 @@ func TestCommand_VisibleFlagCategories(t *testing.T) { } vfc := c.VisibleFlagCategories() - if len(vfc) != 1 { + if len(vfc) != 2 { t.Fatalf("unexpected visible flag categories %+v", vfc) } - if vfc[0].Name() != "cat1" { + if vfc[1].Name() != "cat1" { t.Errorf("expected category name cat1 got %s", vfc[0].Name()) } - if len(vfc[0].Flags()) != 1 { + if len(vfc[1].Flags()) != 1 { t.Fatalf("expected flag category to have just one flag got %+v", vfc[0].Flags()) } - fl := vfc[0].Flags()[0] + fl := vfc[1].Flags()[0] if !reflect.DeepEqual(fl.Names(), []string{"intd", "altd1", "altd2"}) { t.Errorf("unexpected flag %+v", fl.Names()) } diff --git a/help_test.go b/help_test.go index 0a2d9f97fc..8e9396dc8f 100644 --- a/help_test.go +++ b/help_test.go @@ -1520,3 +1520,68 @@ OPTIONS: output.String(), expected) } } + +func TestCategorizedHelp(t *testing.T) { + // Reset HelpPrinter after this test. + defer func(old helpPrinter) { + HelpPrinter = old + }(HelpPrinter) + + output := new(bytes.Buffer) + app := &App{ + Name: "cli.test", + Writer: output, + Action: func(ctx *Context) error { return nil }, + Flags: []Flag{ + &StringFlag{ + Name: "strd", // no category set + }, + &Int64Flag{ + Name: "intd", + Aliases: []string{"altd1", "altd2"}, + Category: "cat1", + }, + }, + } + + c := NewContext(app, nil, nil) + app.Setup() + + HelpPrinter = func(w io.Writer, templ string, data interface{}) { + funcMap := map[string]interface{}{ + "wrapAt": func() int { + return 30 + }, + } + + HelpPrinterCustom(w, templ, data, funcMap) + } + + _ = ShowAppHelp(c) + + expected := `NAME: + cli.test - A new cli + application + +USAGE: + cli.test [global options] command [command options] [arguments...] + +COMMANDS: + help, h Shows a list of + commands or help + for one command + +GLOBAL OPTIONS: + --help, -h show help + --strd value + + cat1 + + --intd value, --altd1 value, --altd2 value (default: 0) + +` + if output.String() != expected { + t.Errorf("Unexpected wrapping, got:\n%s\nexpected:\n%s", + output.String(), expected) + } +}