Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

With a List<> Option in @ArgGroup, group incorrectly appears twice in the synopsis #1065

Closed
kap4lin opened this issue May 23, 2020 · 4 comments
Labels
theme: arg-group An issue or change related to argument groups theme: usagehelp An issue or change related to the usage help message type: bug 🐛
Milestone

Comments

@kap4lin
Copy link

kap4lin commented May 23, 2020

import picocli.CommandLine;
import picocli.CommandLine.*;
import picocli.CommandLine.Model.CommandSpec;

@Command(name = "MyApp")
public class App implements Runnable {

    @ArgGroup(exclusive=true) // or false
    MyGroup myGroup;

    static class MyGroup {
        @Option(names="-A", paramLabel="N", split=",") List<Long> A;
    }

    @Spec CommandSpec spec;

    @Override
    public void run() {
        System.out.printf("OK: %s%n", spec.commandLine().getParseResult().originalArgs());
    }

    public static void main(String[] args) {
        new CommandLine(new App()).execute("-h");
    }
}

Shows the following output:
Usage: MyApp [[-A=N[,N...]] [-A=N[,N...]]...]

Expected output:
Usage: MyApp [-A=N[,N...]]...

Issue initially raised on SO here.

@remkop remkop added this to the 4.4 milestone May 24, 2020
@remkop
Copy link
Owner

remkop commented May 24, 2020

Thank you for raising this.
Looking into it.

@remkop
Copy link
Owner

remkop commented May 24, 2020

The short story

This was a bug. In the next version of picocli, the expected synopsis can be achieved by setting the argument group to exclusive = false.

The long story

This synopsis stuff can get quite complex... Let's break it down.

Option Synopsis

Before we go into argument groups, let's first look at simple options. Picocli shows a different synopsis for required and non-required options, and for single-value and multi-value options.

The below table illustrates. Note especially the notation for required multi-value options. Such options must be specified at least once, but possibly multiple times, and the synopsis reflects this:

               Required         Non-Required
               ---------        ------------
Single value   -x=N             [-x=N]
Multi-value    -x=N [-x=N]...   [-x=N]...

Argument Group Synopsis

Now, let's look at groups. In exclusive groups (the default), all arguments are automatically made required. (There is some history behind this, but basically anything else did not make sense.) In non-exclusive groups, options can be required or optional.

Groups have a multiplicity. The default is multiplicity = "0..1" meaning the group is optional, and this is shown in the synopsis by surrounding the group with [ and ] square brackets.

Now, let's put these together. The table below shows the synopsis for groups with two options, -x and -y:

               Exclusive Group                     Non-Exclusive Group
               ---------------------------------   -------------------
Single value   [-x=N  | -y=M]                      [[-x=N] [-y=M]]
Multi-value    [-x=N [-x=N]... | -x=M [-y=M]...]   [[-x=N]... [-y=M]...]

Split Regex Synopsis

The final element: when the option accepts a split="," regex, the N parameter label becomes N[,N...] in the synopsis.

Problem: synopsis too long

When I execute your example with picocli 4.3.2, I get the following synopsis:

Usage: MyApp [[-A=N[,N...]] [-A=N[,N...]]...]

This is incorrect, and does not follow the specifications above.

With picocli 4.3.3-SNAPSHOT, I get the correct synopsis:

Usage: MyApp [-A=N[,N...] [-A=N[,N...]]...]

Given the above, we now know why: this is the synopsis for a multi-value option on an exclusive group. The option became a required option because the group is exclusive.

Getting a shorter synopsis

With picocli 4.3.3, one idea is to make the group non-exclusive (after all, with only one option, exclusive or non-exclusive does not matter). The program is almost unchanged (exclusive = false instead of true):

@Command(name = "MyApp")
public class App implements Runnable {

    @ArgGroup(exclusive = false) // was:  exclusive=true
    MyGroup myGroup;

    static class MyGroup {
        @Option(names="-A", paramLabel="N", split=",")
        List<Long> A;
    }
    // ...
}

The synopsis of the usage help message now looks like this:

Usage: MyApp [[-A=N[,N...]]...]

I hope this explains things.

@remkop remkop added the status: declined ❌ A suggestion or change that we don't feel we should currently apply label May 24, 2020
@remkop remkop closed this as completed May 24, 2020
remkop added a commit that referenced this issue May 24, 2020
remkop added a commit that referenced this issue May 24, 2020
@remkop remkop reopened this May 24, 2020
@remkop remkop added type: bug 🐛 theme: usagehelp An issue or change related to the usage help message and removed status: declined ❌ A suggestion or change that we don't feel we should currently apply labels May 24, 2020
@remkop
Copy link
Owner

remkop commented May 24, 2020

Apologies, I did some refactoring to reduce duplicate code and this appears to have changed the behavior.

The expected synopsis can be achieved in the next release by making the group non-exclusive.

@remkop remkop closed this as completed May 24, 2020
@remkop
Copy link
Owner

remkop commented May 24, 2020

I updated my answer above.

@remkop remkop added the theme: arg-group An issue or change related to argument groups label May 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
theme: arg-group An issue or change related to argument groups theme: usagehelp An issue or change related to the usage help message type: bug 🐛
Projects
None yet
Development

No branches or pull requests

2 participants