Help System

Airline provides a comprehensive system of help generators that are able to take Airline defined CLIs and output help in a variety of formats. The help system is divided into a number of concepts which cooperate together to make help configurable and extensible.

Concepts

Help Hints

Help Hints are a mechanism by which Restrictions can provide information about their behaviour. This allows for restrictions to be self-documenting and incorporated into help output.

Hints are also used as part of Help Sections.

Help Sections

Help Sections are a mechanism by which @Command annotated classes can add additional sections into help output. This can be used to incorporate arbitrary additional content into your help outputs.

A variety of common help sections are provided out of the box and you can create Custom Help Sections if desired:

Help Generators

Help Generators are classes that take in CLI/Command metadata and output help in some format. The core library includes Text based help generators and additional libraries provide Markdown, HTML, MAN and Bash format help.

Generating Help

In order to generate help you need to create an instance of your desired generator and then pass in the metadata for your CLI/Command e.g.

   Cli<ExampleRunnable> cli = new Cli<ExampleRunnable>(ShipItCli.class);
        
   CliGlobalUsageGenerator<ExampleRunnable> helpGenerator = new CliGlobalUsageGenerator<>();
   try {
       helpGenerator.usage(cli.getMetadata(), System.out);
   } catch (IOException e) {
       e.printStackTrace();
   }

In this example we output the help to System.out but we could pass any OutputStream we desired.

For single commands we could use CliCommandUsageGenerator instead.

Incorporating Help into your CLI

Airline provides a couple of ways you can incorporate help into your CLIs/commands to make this easily accessible to your end users.

HelpOption

For single commands you can add the HelpOption to your command classes e.g.

@Command(name = "parent", description = "A parent command")
public class Parent implements ExampleRunnable {

    @AirlineModule
    protected HelpOption<ExampleRunnable> help;

    @Option(name = "--parent", description = "An option provided by the parent")
    private boolean parent;

    public static void main(String[] args) {
        ExampleExecutor.executeSingleCommand(Parent.class, args);
    }

    @Override
    public int run() {
        if (!help.showHelpIfRequested()) {
            System.out.println("--parent was " + (this.parent ? "set" : "not set"));
        }
        return 0;
    }

}

We use our normal Composition mechanism to add in HelpOption to our command class. This will provide a -h/--help option in our command. We can then use the help.showHelpIfRequested() method to check whether help was requested and if not proceed normally. If this method returns true then help has been requested and output to the user.

Help class

The Help class is a pre-built @Command class that provides intelligent help for CLIs. You can simply add this to your CLIs as one of your commands and it will add a help command to your CLI. This command will select the appropriate help to display depending on the arguments provided. For example calling your-cli help will display help for the entire CLI, whereas calling your-cli help your-command will display command specific help for your-command.

If you can’t incorporate Help directly as a command because it does not extend/implement the base command type for your CLI you can still use it indirectly. If you are using a base interface then you can simply extend the class and add implements YourInterface plus any necessary implementation deferring to the run() method e.g.


public class CustomHelpCommand extends Help implements YourInterface {
  @Override
  public void yourMethod() {
    super.run();
  }
}

Alternatively the class also provides a number of static methods that you can call from your own command if you can’t extend it, for example when using a base class for your commands, e.g.

Help.help(cli.getMetadata(), args, System.out)

Where args is a List<String> containing the command name(s) that help is being requested on. So for our earlier example we might call the following:

Help.help(cli.getMetadata(), Collections.singletonList("your-command"), System.out)

Help and Errors

Note that when trying to incorporate help into a CLI one common problem that occurs is that you add restrictions which end up get violated when users try to just invoke help on your commands. This is particularly the case when using HelpOption. To avoid this you will need to customise the error handler as discussed in the Error Handling documentation and do something like the following:

public static void main(String[] args) {
        com.github.rvesse.airline.Cli<ExampleRunnable> cli = new com.github.rvesse.airline.Cli<ExampleRunnable>(ShipItCli.class);
        try {
            // Parse with a result to allow us to inspect the results of parsing
            ParseResult<ExampleRunnable> result = cli.parseWithResult(args);
            if (result.wasSuccessful()) {
                // Parsed successfully, so just run the command and exit
                System.exit(result.getCommand().run());
            } else {
                // Parsing failed
                // Display errors and then the help information
                System.err.println(String.format("%d errors encountered:", result.getErrors().size()));
                int i = 1;
                for (ParseException e : result.getErrors()) {
                    System.err.println(String.format("Error %d: %s", i, e.getMessage()));
                    i++;
                }
                
                System.err.println();
                
                com.github.rvesse.airline.help.Help.<ExampleRunnable>help(cli.getMetadata(), Arrays.asList(args), System.err);
            }
        } catch (Exception e) {
            // Errors should be being collected so if anything is thrown it is unexpected
            System.err.println(String.format("Unexpected error: %s", e.getMessage()));
            e.printStackTrace(System.err);
        }
        
        // If we got here we are exiting abnormally
        System.exit(1);
    }

Improving this Documentation

This documentation is itself open source and lives in GitHub under the docs/ directory.

I am not a professional technical writer and as the developer of this software I can often make assumptions of knowledge that you as a user reading this may not have. Improvements to the documentation are always welcome, if you have suggestions for the documentation please submit pull requests to the main branch.