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

Built-in Help being used on subcommands, still case-sensitive even if case-sensitivity disabled #1156

Closed
Shadowsoftime99 opened this issue Aug 26, 2020 · 9 comments · Fixed by #1172
Milestone

Comments

@Shadowsoftime99
Copy link

Hey, first of all I'll start with saying that I'm just about done using Pico for a work project and it's been absolutely amazing.

One small inconsistency that I still have left (and just put down in a "common issues" wiki section) is that even with case-sensitivity disabled, for example:

[Tools] help SUBCOMMAND

Gives an "Unknown subcommand" error, despite "subcommand" existing with different capitalization. This is inconsistent with everything else, and the only thing that the user needs to be case-sensitive for, as:

[Tools] HELP subcommand
[Tools] SUBCOMMAND
[Tools] subcommand -OpTiOn

All work perfectly fine. It's just when trying to run help on a subcommand, the subcommand's name must be perfect.

I have a hunch that the issue lies in the implementation of the Help command, but am not sure. I'm open to working on this myself if I have some free time in a few weeks, but figured I'd put this issue out there now.

@remkop
Copy link
Owner

remkop commented Aug 26, 2020

Thank you for raising this!
I suspect your hunch is probably right. :-)
If you feel like providing a PR for this, that would be great!

Thank you for the kind words about picocli! Can I ask: what is it that you like about picocli, and what would you do if picocli didn't exist?

@Shadowsoftime99
Copy link
Author

I like the simplicity of Pico, and how elegant the structure of the code is when using the subcommand structure. Also the availability of features that I'm looking for (I've had bad experiences in the past with Java libs where the answer to how to make a somewhat basic feature was "extend this class but a bunch of the stuff in it is private instead of protected so you'll need to duplicate a lot of code and make a mess").

If Pico didn't exist, I probably would've either used gone through with my plan of manually taking the user's input and handling the args array manually (ugh), or gone to find another CLI library.

Pico was actually a suggestion from a co-worker, when I mentioned my plan for input parsing he said that trying to do it myself was insane and listed a few Java CLI libraries.

Thanks for this interest in the user experience! I'll circle back to a PR for this later.

@remkop
Copy link
Owner

remkop commented Aug 26, 2020

Thanks for the quick feedback!
Don't forget to star ⭐ picocli on GitHub if you like the project! 😉

@remkop remkop added this to the 4.6 milestone Aug 26, 2020
@NewbieOrange
Copy link
Contributor

NewbieOrange commented Sep 11, 2020

It seems the issue is related to how CommandLine returns the subcommands.

CommnadLine.java: Line 367
public Map<String, CommandLine> getSubcommands() {
        return new LinkedHashMap<String, CommandLine>(getCommandSpec().subcommands());
}

A new LinkedHashMap is returned, while the impl actually uses the case insensitive-able map now.

Now there are two options, one is changing the bahaviour of CommandLine.getSubcommand() (and options?) to return a case aware map (or simply the unmodifiable-wrap from CommandSepc.subcommands()), another is to make HelpCommand get subcommand from CommandSpec directly.

I think we should probably correct the bahaviour of CommandLine.getSubcommand(). Should it return the unmodifiable map (who will want to make changes to the map since it is not affecting anything), or keep the current "return a modifiable copy" state for (maybe) backward-compatibility?

I will make a PR after hearing your feedbacks. @remkop

Edit: typo

@remkop
Copy link
Owner

remkop commented Sep 11, 2020

@NewbieOrange thanks for the analysis!
I agree, we could fix this particular issue by changing HelpCommand to something like this:

Index: src/main/java/picocli/CommandLine.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/main/java/picocli/CommandLine.java	(date 1599786814496)
+++ src/main/java/picocli/CommandLine.java	(date 1599786814496)
@@ -13924,7 +13924,10 @@
             if (parent == null) { return; }
             Help.ColorScheme colors = colorScheme != null ? colorScheme : Help.defaultColorScheme(ansi);
             if (commands.length > 0) {
-                CommandLine subcommand = parent.getSubcommands().get(commands[0]);
+                String fullName = AbbreviationMatcher.match(parent.getCommandSpec().subcommands().keySet(),
+                        commands[0], parent.getCommandSpec().subcommandsCaseInsensitive(), self);
+                CommandLine subcommand = parent.getSubcommands().get(fullName);
+
                 if (subcommand != null) {
                     if (outWriter != null) {
                         subcommand.usage(outWriter, colors);

But it may also be a good idea to return a CaseAwareLinkedMap from CommandLine::getSubcommands. (There is no method CommandLine::getOptions, so we don't need to worry about that.)

I prefer to keep the current behaviour of returning a modifiable map. I don't want to break existing applications that rely on the fact that it is modifiable.

So, we probably need to add a CaseAwareLinkedMap copy constructor.
That way, we can change the implementation of CommandLine::getSubcommands to something like this:

Index: src/main/java/picocli/CommandLine.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- src/main/java/picocli/CommandLine.java	(date 1599812971632)
+++ src/main/java/picocli/CommandLine.java	(date 1599812971632)
@@ -365,7 +365,7 @@
      * @since 0.9.7
      */
     public Map<String, CommandLine> getSubcommands() {
-        return new LinkedHashMap<String, CommandLine>(getCommandSpec().subcommands());
+        return new CaseAwareLinkedMap<String, CommandLine>(getCommandSpec().commands);
     }
     /**
      * Returns the command that this is a subcommand of, or {@code null} if this is a top-level command.

@NewbieOrange
Copy link
Contributor

NewbieOrange commented Sep 11, 2020

@remkop #1172 should fix this HelpCommand issue. The CommandLine.getSubcommands() func now returns a copy of CaseAwareLinkMap.

The Help class still uses LinkedHashMap and do not care about the case-sensitivity from the CommandSpec. Should the case-sensitivity setting also affect it?

@NewbieOrange
Copy link
Contributor

CommandLine.java: Line 14078
public static class Help {
    ... ...
    private final Map<String, Help> allCommands = new LinkedHashMap<String, Help>();
    private final Map<String, Help> visibleCommands = new LinkedHashMap<String, Help>();
}

@remkop
Copy link
Owner

remkop commented Sep 13, 2020

This is now fixed thanks to @NewbieOrange's pull request. The fix will be in the next release.

Thank you @NewbieOrange for the pull request and thank you @Shadowsoftime99 for raising this!

@remkop
Copy link
Owner

remkop commented Oct 14, 2020

Picocli 4.5.2, which contains the fix for this issue, has been released.
Enjoy!

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

Successfully merging a pull request may close this issue.

3 participants