Skip to content

Refactor help API#19

Draft
mfridman wants to merge 5 commits intomainfrom
mf/usage-api
Draft

Refactor help API#19
mfridman wants to merge 5 commits intomainfrom
mf/usage-api

Conversation

@mfridman
Copy link
Copy Markdown
Collaborator

@mfridman mfridman commented Apr 26, 2026

This PR reshapes help customization and usage-error handling against the API on main.

On main, custom help is Command.UsageFunc func(*Command) string, default help lives behind DefaultUsage, help detection re-exports flag.ErrHelp as cli.ErrHelp, command descriptions use ShortHelp, and flag metadata uses FlagOption / Command.FlagOptions. This branch keeps core help automatic and string-based, while moving manual rendering and structured composition to the optional usage package:

  • Command.Help func(*Command) string replaces UsageFunc for custom help.
  • Command.Description replaces ShortHelp for command descriptions.
  • --help and cli.UsageErrorf still print command help automatically.
  • Parse returns flag.ErrHelp when callers handle help themselves.
  • usage.Help(cmd) renders the same help text ParseAndRun would print for --help.
  • usage.New(cmd) builds a composable usage.Document for adding sections like Examples.
  • State.Cmd exposes the resolved command inside Exec.
  • FlagConfig / Command.FlagConfigs replace FlagOption / Command.FlagOptions.

Why: the default path stays small, automatic, and out of the way. Callers that want richer help can opt into usage without making structured documents part of the core API.

Examples:

// main
cmd.UsageFunc = func(c *cli.Command) string {
    return "custom help"
}

// this PR
cmd.Help = func(c *cli.Command) string {
    return "custom help"
}
// main
ShortHelp: "print a greeting",

// this PR
Description: "print a greeting",
// keep the default help and add examples
cmd.Help = func(c *cli.Command) string {
    doc := usage.New(c)
    doc = append(doc, usage.Lines("Examples:", "greet margo"))
    return doc.String()
}
// handle help manually after cli.Parse
if err := cli.Parse(root, args); err != nil {
    if errors.Is(err, flag.ErrHelp) {
        fmt.Fprintln(stdout, usage.Help(root))
        return nil
    }
    return err
}
// print command help for invalid args discovered in Exec
Exec: func(ctx context.Context, s *cli.State) error {
    if len(s.Args) == 0 {
        return cli.UsageErrorf("must supply a name")
    }
    return nil
},

Breaking changes:

  • Remove Command.UsageFunc; use Command.Help.
  • Rename Command.ShortHelp to Command.Description.
  • Remove DefaultUsage and the top-level Usage function; use usage.Help(cmd) or usage.New(cmd).
  • Remove cli.ErrHelp; use flag.ErrHelp.
  • Rename FlagOption to FlagConfig and Command.FlagOptions to Command.FlagConfigs.

Closes #4, #14, #15.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add ability to get command information from *State

1 participant