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

Runtime control of hidden options #736

Closed
akoscz opened this issue Jun 18, 2019 · 6 comments
Closed

Runtime control of hidden options #736

akoscz opened this issue Jun 18, 2019 · 6 comments

Comments

@akoscz
Copy link

akoscz commented Jun 18, 2019

Is there a way to control the hidden attribute of a command line option at runtime? The use case I'm working with requires that an option should only be available to the user when executing the application on a specific operating system. For example:

private static final boolean isDarwin = Utils.isOSType(OSType.Darwin);

@CommandLine.Option(names = {"-o", "--open"}, 
    description = "On OSX open the finder to the data dir that was downloaded.", 
    showDefaultValue = CommandLine.Help.Visibility.ALWAYS, 
    hidden = !isDarwin) // hide this option on all OS's other than Darwin (OSX)
boolean open = false;

The above example would be ideal, although the compiler complains that the value for the hidden attribute must be a constant expression. I'm guessing it has to do with the way the annotation processor generates the code at compile time.

Since the above isn't supported, can I get at the CommandLine.Option object and toggle the hidden attribute at runtime, let's say like in the constructor of my object?

Any feedback is welcome. Thanks in advance!

@remkop
Copy link
Owner

remkop commented Jun 19, 2019

Changing the model is currently not easy.
I’m looking into whether it’s possible to change the rendering of the usage help message so that hidden options are shown. I’ll post a recipe when I have something.

@remkop
Copy link
Owner

remkop commented Jun 19, 2019

I had a look and I need to make some changes to the CommandLine.Help class and its components to facilitate this. Your timing is good: this will require some API changes, so it's good to do this in a major release like the upcoming version 4.0.

My goal is to change things so that you can customize like this:

import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Help;
import picocli.CommandLine.Help.ColorScheme;
import picocli.CommandLine.IHelpFactory;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Model.OptionSpec;
import picocli.CommandLine.Option;

@Command(name = "show-hidden-options")
public class ShowHiddenOptions {
    @Option(names = "--visible") String visible;
    @Option(names = "--hidden", hidden = true) String hidden;

    public static void main(String[] args) {
        final boolean IS_MAC_OS = System.getProperty("os.name").toLowerCase().contains("mac");

        CommandLine cmd = new CommandLine(new ShowHiddenOptions());
        cmd.setHelpFactory(new IHelpFactory() {
            @Override
            public Help create(CommandSpec commandSpec, ColorScheme colorScheme) {
                return new Help(commandSpec, colorScheme) {
                    @Override
                    protected boolean isHidden(OptionSpec option) {
                        return option.hidden() && !IS_MAC_OS;
                    }
                };
            }
        });

        cmd.usage(System.out);
    }
}

I hope to have something for you later this week.

@remkop
Copy link
Owner

remkop commented Jun 24, 2019

Sorry for the delay.
I took a stab at implementing this in the Help API but I started to have doubts that this is really the right way to go.

Allowing applications to change the model is a more general and more powerful solution.

At the moment OptionSpec instances are immutable, and I would like to keep it that way, but the CommandSpec is not immutable. So, I am thinking to add the ability to replace an OptionSpec.

Something like this:

CommandLine cmd = new CommandLine(new MyApp());

// replace the old "--open" option with a different one that is not hidden on Darwin
CommandSpec spec = cmd.getCommandSpec();
OptionSpec old = spec.findOption("--open");
OptionSpec newOpenOption = OptionSpec.builder(old).hidden(!isDarwin).build();
spec.remove(old);
spec.add(newOpenOption);

cmd.execute(args);

@remkop remkop closed this as completed in 56959b8 Jul 11, 2019
@remkop
Copy link
Owner

remkop commented Jul 11, 2019

I added a CommandSpec.remove(ArgSpec) method that makes it possible to do thing like in the above example: remove option, make a copy with a different attribute, then add the copy back.

See also this test.
Can you verify?

@JPedroBorges
Copy link

Is there a way to accomplish the same but with an ArgSpec that is part of an ArgGroup?

When removing the ArgSpec from the CommandSpec:

public CommandSpec remove(ArgSpec arg) {
  if (arg.group() != null) {
    throw new UnsupportedOperationException("Cannot remove ArgSpec that is part of an ArgGroup");
  }
...

As far as I can see the ArgGroup is immutable as well.

Is there any workaround?

Thank you in advance.

@remkop
Copy link
Owner

remkop commented Aug 25, 2020

@JPedroBorges Currently there’s no way to modify argument groups. Can you raise a separate ticket for this?

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

3 participants