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

Nested group outside of the section in the help #778

Closed
teoincontatto opened this issue Aug 6, 2019 · 11 comments
Closed

Nested group outside of the section in the help #778

teoincontatto opened this issue Aug 6, 2019 · 11 comments
Milestone

Comments

@teoincontatto
Copy link

Using version 4.0.1 and 4.0.2-SNAPSHOT when a nested group is present inside another one the nested group's options are printed outside of the section in the help:

  • Test code:
@picocli.CommandLine.Command(name = "test-with-nested-group",
    description = "Picocli section test with nested group.",
    abbreviateSynopsis = true,
    sortOptions = false)
public static class Test {
  @ArgGroup(validate = false, heading = "A1%n")
  private final A1 a1 = new A1();

  private static class A1 {
    @ArgGroup(exclusive = true)
    private final NestedA1 nestedA1 = new NestedA1();

    private static class NestedA1 {
      @Option(names = {"-ta1", "--test-a1"})
      private String testA1;
      @Option(names = {"-ta2", "--test-a2"})
      private String testA2;
    }
  }

  @ArgGroup(validate = false, heading = "A2%n")
  private final A2 a2 = new A2();

  private static class A2 {
    @Option(names = {"-tb1", "--test-b1"})
    private String testB1;
    @Option(names = {"-tb2", "--test-b2"})
    private String testB2;
  }

  /**
   * Picocli test.
   */
  public static void main(String[] args) {
    new CommandLine(new Test()).usage(System.out);
  }
}
  • Output:
Usage: test-with-nested-group [OPTIONS]
Picocli section test with nested group.
      -ta1, --test-a1=<testA1>

      -ta2, --test-a2=<testA2>

A1
A2
      -tb1, --test-b1=<testB1>

      -tb2, --test-b2=<testB2>

Originally posted by @teoincontatto in #776 (comment)

@remkop remkop added this to the 4.0.2 milestone Aug 6, 2019
@remkop
Copy link
Owner

remkop commented Aug 6, 2019

Hmm, yes, but is there really is a problem? The A1 group itself does not have any options. The -ta1 and -ta2 options are in the NestedA1 nested subgroup, but that subgroup does not have a heading. If we give the nested subgroup a heading, like this:

...
  private static class A1 {
    @ArgGroup(exclusive = true, heading = "NestedA1%n")
    private final NestedA1 nestedA1 = new NestedA1();
...

Then the resulting usage message looks like this:

Usage: test-with-nested-group [OPTIONS]
Picocli section test with nested group.
NestedA1
      -ta1, --test-a1=<testA1>

      -ta2, --test-a2=<testA2>

A1
A2
      -tb1, --test-b1=<testB1>

      -tb2, --test-b2=<testB2>

I believe this is reasonable. Do you think it should behave differently?

@teoincontatto
Copy link
Author

I see. Actually, this does not fit my use case. What I was trying to achieve was a section in the help to specify parameters to connect to a database (like host, port and so forth) and I have some parameters that are exclusive to indicate the connection mode (ssl, ssl with full cert validation, no ssl) that should appear in the same section.

@remkop
Copy link
Owner

remkop commented Aug 6, 2019

What would you like the usage help to look like for your use case?

@teoincontatto
Copy link
Author

Well, actually if I could include an excluded group inside of a section with other parameters that would fit the use case. As an example the output I would expect referred to the initial test of this issue would be:

Usage: test-with-nested-group [OPTIONS]
Picocli section test with nested group.
A1
      -ta1, --test-a1=<testA1>

      -ta2, --test-a2=<testA2>
A2
      -tb1, --test-b1=<testB1>

      -tb2, --test-b2=<testB2>

@remkop
Copy link
Owner

remkop commented Aug 6, 2019

Not sure that I follow...
In the example, the A1 class doesn’t seem to serve any purpose. It could be removed, and a heading could be added to the NestedA1 group, with the same parsing behavior and the desired usage help message.

If your actual use case is different it may be more productive to discuss the actual use case instead of an artificial example. Would that be possible?

@teoincontatto
Copy link
Author

I'll prefer to get stuck to artificial example. In order to resemble better my use case I'll try to adapt the example with my use case:

  • Code:
import picocli.CommandLine;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Option;

@picocli.CommandLine.Command(name = "test-with-nested-group",
    description = "Picocli section test with nested group.",
    abbreviateSynopsis = true,
    sortOptions = false)
public class Test {
  @ArgGroup(validate = false, heading = "database%n")
  private final Database database = new Database();

  private static class Database {
    @Option(names = {"--host"})
    private String host;

    @Option(names = {"--port"})
    private String port;

    @Option(names = {"--database"})
    private String database;

    @Option(names = {"--user"})
    private String user;

    @Option(names = {"--password"})
    private String password;

    @ArgGroup(exclusive = true)
    private final SslMode sslMode = new SslMode();

    private static class SslMode {
      @Option(names = {"--no-ssl"})
      private boolean noSsl;
      @Option(names = {"--use-ssl"})
      private boolean useSsl;
      @Option(names = {"--use-strict-ssl"})
      private boolean useStrictSsl;
    }
  }

  /**
   * Picocli test.
   */
  public static void main(String[] args) {
    new CommandLine(new Test()).usage(System.out);
  }
}
  • Actual output:
Usage: test-with-nested-group [OPTIONS]
Picocli section test with nested group.
      --no-ssl
      --use-ssl
      --use-strict-ssl
database
      --host=<host>
      --port=<port>
      --database=<database>

      --user=<user>
      --password=<password>
  • Expected output:
Usage: test-with-nested-group [OPTIONS]
Picocli section test with nested group.
database
      --host=<host>
      --port=<port>
      --database=<database>
      --user=<user>
      --password=<password>
      --no-ssl
      --use-ssl
      --use-strict-ssl

@remkop
Copy link
Owner

remkop commented Aug 6, 2019

Thank you for the clarification. I need some time to think about this.

@remkop
Copy link
Owner

remkop commented Aug 6, 2019

I found a way to accomplish this with the current version of picocli:
you can specify the order attribute on the @ArgGroup annotation to manually control the ordering. This attribute only works for argument groups with a non-null heading, but you can specify an empty string for the heading. For example:

import org.junit.Test;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;

import static org.junit.Assert.assertEquals;

public class Issue778ArgGroupHelpSections {

    @Command(name = "test-with-nested-group",
            description = "Picocli section test with nested group.",
            abbreviateSynopsis = true,
            sortOptions = false)
    public static class TestCommand {

        @ArgGroup(validate = false, heading = "database%n", order = 1)
        private final Database database = new Database();

        private static class Database {
            @Option(names = {"--host"})
            private String host;

            @Option(names = {"--port"})
            private String port;

            @Option(names = {"--database"})
            private String db;

            @Option(names = {"--user"})
            private String user;

            @Option(names = {"--password"})
            private String pwd;
        }

        @ArgGroup(exclusive = true, heading = "", order = 2)
        private final SslMode sslMode = new SslMode();

        private static class SslMode {
            @Option(names = {"--no-ssl"})
            private boolean noSsl;
            @Option(names = {"--use-ssl"})
            private boolean useSsl;
            @Option(names = {"--use-strict-ssl"})
            private boolean useStrictSsl;
        }
    }

    @Test
    public void testUsage() {
        String expected = String.format("" +
                "Usage: test-with-nested-group [OPTIONS]%n" +
                "Picocli section test with nested group.%n" +
                "database%n" +
                "      --host=<host>%n" +
                "      --port=<port>%n" +
                "      --database=<db>%n" +
                "      --user=<user>%n" +
                "      --password=<pwd>%n" +
                "      --no-ssl%n" +
                "      --use-ssl%n" +
                "      --use-strict-ssl%n");
        String actual = new CommandLine(new TestCommand())
                .getUsageMessage(CommandLine.Help.Ansi.OFF);
        assertEquals(expected, actual);
    }
}

Would that work for you?

We should probably improve the documentation in this area. Suggestions or pull requests welcome!

@remkop
Copy link
Owner

remkop commented Aug 6, 2019

Looking closer, the javadoc for ArgGroup.order is wrong: it does not matter whether @Command(sortOptions=true).
What matters is that the @ArgGroup has a non-null heading: if that is the case, the option in the argument group are grouped together in the usage help. Otherwise they are mixed in with the other options in the command.

@teoincontatto
Copy link
Author

The option you mention of using the order inside @ArgGroup would totally work for me. Thanks for the hint and for your time helping me out, I really appreciated! 😃

@remkop
Copy link
Owner

remkop commented Aug 8, 2019

I updated the user manual (this is live already) and the javadoc (for the next release).

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

No branches or pull requests

2 participants