Command-line-api: 鈥攈elp doesn鈥檛 list additional commands when Command.Description is empty

Created on 12 Jul 2019  路  8Comments  路  Source: dotnet/command-line-api

I鈥檝e not yet debugged into why this is but the current behaviour seems at least unexpected if not wrong.

Using an amended version of the sample code and then adding additional commands CommandA and CommandB, see below:

using System.CommandLine;
using System.CommandLine.Invocation;
using System.IO;
using System;

namespace s.cl
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create some options and a parser
            Option optionThatTakesInt = new Option(
                "--int-option",
                "An option whose argument is parsed as an int")
                {
                    Argument = new Argument<int>()
                };
            Option optionThatTakesBool = new Option(
                "--bool-option",
                "An option whose argument is parsed as a bool")
                {
                    Argument = new Argument<bool>()
                };
            Option optionThatTakesFileInfo = new Option(
                "--file-option",
                "An option whose argument is parsed as a FileInfo")
                {
                    Argument = new Argument<FileInfo>()
                };

            // Add them to the root command
            var rootCommand = new RootCommand();
            rootCommand.Description = "My sample app";
            rootCommand.AddOption(optionThatTakesInt);
            rootCommand.AddOption(optionThatTakesBool);
            rootCommand.AddOption(optionThatTakesFileInfo);

            var subCommandA = new Command("CommandA");
            subCommandA.AddAlias("ca");

            subCommandA.Handler = CommandHandler.Create<int, bool, FileInfo>((intOption, boolOption, fileOption) =>
            {
                Console.WriteLine($"Called CommandA");
            });
            subCommandA.AddOption(new Option("-x", "Indicates whether x"));
            subCommandA.TreatUnmatchedTokensAsErrors = false;

            rootCommand.AddCommand(subCommandA);

            var subCommandB = new Command("CommandB");
            subCommandB.AddAlias("cb");
            subCommandB.Handler = CommandHandler.Create<int, bool, FileInfo>((intOption, boolOption, fileOption) =>
            {
                Console.WriteLine($"Called CommandB");
            });
            subCommandB.AddOption(new Option("-x", "Indicates whether x"));
            subCommandB.TreatUnmatchedTokensAsErrors = false;

            rootCommand.AddCommand(subCommandB);

            rootCommand.Handler = CommandHandler.Create<int, bool, FileInfo>((intOption, boolOption, fileOption) =>
            {
                Console.WriteLine($"The value for --int-option is: {intOption}");
                Console.WriteLine($"The value for --bool-option is: {boolOption}");
                Console.WriteLine($"The value for --file-option is: {fileOption?.FullName ?? "null"}");
            });

            // Parse the incoming args and invoke the handler
            Environment.Exit(rootCommand.InvokeAsync(args).Result);
        }
    }
}

After compiling and running you get the following output

$ dotnet run -- --help
s.cl:
  My sample app

Usage:
  s.cl [options]

Options:
  --int-option <int-option>      An option whose argument is parsed as an int
  --bool-option                  An option whose argument is parsed as a bool
  --file-option <file-option>    An option whose argument is parsed as a FileInfo
  --version                      Display version information

There is no mention of the extra commands, CommandA and CommandB

But if I know the commands are there I can do the following

$ dotnet run -- CommandA
Called CommandA

and then

$ dotnet run -- CommandA --help
Usage:
  s.cl CommandA [options] [[--] <additional arguments>...]]

Options:
  -x    Indicates whether x

Additional Arguments:
  Arguments passed to the application that is being run.`

So they are there.

Quite happy that I might be missing something obvious, but its not obvious to me :)

(Thanks for the library though 馃憤 )

bug good first issue help wanted

Most helpful comment

If you add a description to the command, it will show up in help.

This looks like an accidental holdover from before Symbol.IsHidden was introduced. Originally, an empty description indicated that the symbol should be hidden from help. The behavior should be updated to behave the way it does for Option, which is that help shows the option even if there's no description:

> dotnet run -- /?                                                                                                                                                      My sample app

Usage:
  c [options] [command]

Options:
  --int-option <int-option>
  --bool-option                  An option whose argument is parsed as a bool
  --file-option <file-option>    An option whose argument is parsed as a FileInfo        --version                      Display version information

Commands:
  ca, CommandA    Help for CommandA

All 8 comments

If you add a description to the command, it will show up in help.

This looks like an accidental holdover from before Symbol.IsHidden was introduced. Originally, an empty description indicated that the symbol should be hidden from help. The behavior should be updated to behave the way it does for Option, which is that help shows the option even if there's no description:

> dotnet run -- /?                                                                                                                                                      My sample app

Usage:
  c [options] [command]

Options:
  --int-option <int-option>
  --bool-option                  An option whose argument is parsed as a bool
  --file-option <file-option>    An option whose argument is parsed as a FileInfo        --version                      Display version information

Commands:
  ca, CommandA    Help for CommandA

Ah, fantastic. Working now thanks.
Is there a ticket for the issue you describe I could follow, potentially contribute to?

We can use this issue to track it. I renamed it to make that clearer and added the "good first issue" and "help wanted" tags.

I'd like to start working on this issue if no one else is doing it at the moment. I'm guessing it's the
ShouldShowHelp method below (in src/System.CommandLine/Help/HelpBuilder.cs) that needs to be changed.
If I understand correctly, this method should return true for all symbols unless the IsHidden flag is set to true?

```
internal bool ShouldShowHelp(ISymbol symbol)
{
if (symbol.IsHidden)
{
return false;
}

if (symbol is IArgument)
{
    return !symbol.IsHidden;
}
else
{
    return !symbol.IsHidden &&
           (!string.IsNullOrWhiteSpace(symbol.Description) ||
            symbol.Arguments().Any(ShouldShowHelp));
}

}

That would be great!

That sounds like the correct fix. Let us know if you want any pointers.

I forked the repo, but a couple of tests are failing. Anyone else having the same problems?
The tests seem to fail when calling InvokeMethod or InvokeMethodAsync. More details below.

Test class:
System.CommandLine.DragonFruit.Tests.CommandLineTests
Test cases failing:
It_handles_uncaught_exceptions
It_handles_uncaught_exceptions_synchronously
It_shows_error_without_invoking_method
It_shows_error_without_invoking_method_synchronously
Stack traces start with (same for all tests):
System.ArgumentException : _invocationTarget at System.CommandLine.Binding.MethodInfoHandlerDescriptor.GetCommandHandler() in /command-line-api/src/System.CommandLine/Binding/MethodInfoHandlerDescriptor.cs:line 47 at System.CommandLine.Invocation.CommandHandler.Create(MethodInfo method, Object target) in /command-line-api/src/System.CommandLine/Invocation/CommandHandler.cs:line 16

I have a fix for those tests here: #591

Great, thank you!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jnm2 picture jnm2  路  6Comments

shaggygi picture shaggygi  路  5Comments

erichiller picture erichiller  路  3Comments

jonsequitur picture jonsequitur  路  6Comments

rgueldenpfennig picture rgueldenpfennig  路  4Comments