picocli icon indicating copy to clipboard operation
picocli copied to clipboard

Hide the default System section from the HELP command

Open allguitars opened this issue 3 years ago • 5 comments

Hi,

I am currently working on a project where we have implemented a few custom commands. When I just type help command (without any subcommand), it displays two sections. One is the default System commands that are registered inherently showing help and exit commands. The other section shows our custom commands.

However, our application is currently not supporting the exit command so we want to hide this part. How do I remove or hide the System section from the help menu?

2021-09-11_001

allguitars avatar Sep 10 '21 19:09 allguitars

Hi! That’s hard to say without seeing the code, but I guess you would undo (or comment out) what you did to show these sections.

remkop avatar Sep 10 '21 21:09 remkop

Hi Remko,

Here is how we build up the command registries.

@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
class NormalMode implements CliMode {
    private MessageSource i18n
    private Supplier<Path> pwd
    private CommandLine parentCommandLine

    @Autowired
    NormalMode(
        MessageSource messageSource,
        @Qualifier("currentWorkingDir") Supplier<Path> pwd,
        @Qualifier("rootCommandLine") CommandLine rootCommandLine,
        SetupCommand setupCommand,
        SetFactoryCommand setFactoryCommand,
        ShowCommand showCommand,
        ServiceCommand serviceCommand,
        BackDoorCommand backDoorCommand
    ) {
        this.i18n = messageSource
        this.pwd = pwd
        this.parentCommandLine = rootCommandLine
        attachSubCommands(
            rootCommandLine,
            setupCommand,
            setFactoryCommand,
            showCommand,
            serviceCommand,
            backDoorCommand
        )
    }

    protected void attachSubCommands(CommandLine commandLine, CliCommand<?>... subCommands) {
        for (CliCommand<?> command : subCommands) {
            commandLine.addSubcommand(command)
        }
    }

    @Override
    Supplier<String> supplyPromptString() {
        return () -> i18n.getMessage("mode.normal.prompt", null, Locale.getDefault())
    }

    @Override
    Supplier<Path> supplyCurrentWorkingDirectory() {
        return pwd
    }

    @Override
    CommandRegistry getCommandRegistry() {
        return new PicocliCommands(pwd, parentCommandLine) {
            @Override
            String name() {
                return i18n.getMessage("mode.normal.name", null, Locale.getDefault())
            }
        }
    }
}

Let me paste one of the command implementation here. The business logic was made under its subcommand. And I include the CommandLine.HelpCommand class here so that we can allow the user to type show help to see what subcommands we offer.

@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
@Command(name = "show",
        subcommands = [
            ShowInterfaceCommand,
            ShowPm2ServiceCommand,
            ShowSystemCommand,
            CommandLine.HelpCommand ],
        description = "Show system information")
@Slf4j
final class ShowCommand extends AbstractCliCommand<Integer> {

    @Override
    Integer call() throws Exception {
        return 0
    }

}

At the point of reading the user's commands as shown below, we instantiate a SystemRegistryImpl object, set the command registries and create the LineReader object and then start to continuously read in user's command and execute it.

    @Override
    void repl(CliMode mode) throws EndOfFileException, Exception {
        SystemRegistry aggregator = new SystemRegistryImpl(parser, terminal, mode.supplyCurrentWorkingDirectory(),
                null)
        aggregator.setCommandRegistries(mode.getCommandRegistry())

        LineReader lineReader = LineReaderBuilder
                .builder()
                .terminal(terminal)
                .parser(parser)
                .option(Option.CASE_INSENSITIVE, true)
                .option(Option.AUTO_GROUP, false)
                .option(Option.GROUP, false)
                .completer(aggregator.completer()).variable(LineReader.LIST_MAX, 50).build()

        while (true) {
            try {
                aggregator.cleanUp()
                String line = lineReader.readLine(mode.supplyPromptString().get())
                aggregator.execute(line)
            } catch (UserInterruptException ctrlc) {
                log.error("The user pressed Ctrl+C.", ctrlc)
 
           ...          

We did not interfere with how the API will perform the root help behavior, that is, when we just type the help command, it prints out all the commands including the System section shown in the screenshot. We understand that it should print out all the custom commands listed under the Normal Mode section, but how can we get rid of the System section so the user will not know they can type the exit or help command.

Cheers, Dave

allguitars avatar Sep 11 '21 11:09 allguitars

Oh I see now, this is a JLine3 application, that was not clear to me initially.

Not sure if this is possible, but it may be. I believe that the Exit command is a JLine3 command, that does not use the picocli API... You will have to dig a bit into the integration code that combines JLine3 with picocli, and where these commands are added.

remkop avatar Sep 11 '21 12:09 remkop

@allguitars it has been a while so it may not be on your radar any longer, but another suggestion I could make is to contact the JLine3 project and ask if they have any suggestions.

remkop avatar Sep 28 '21 01:09 remkop

@allguitars Is this still an issue for you or can I close this ticket?

remkop avatar Jan 19 '22 05:01 remkop