Skip to content

How do you have your program show the Help menu when the user passes no command line arguments.  #1449

Open
@kasajian

Description

@kasajian

Right now, when the specifies no command line arguments at all, I output "type -? for help". I'd like it to actually show the help. How can you invoke the help programmatically?

Activity

elgonzo

elgonzo commented on Oct 26, 2021

@elgonzo
Contributor

Something along the lines of this?

using System.CommandLine;
using System.CommandLine.Help;

static void Main(string[] args)
{
    //
    // Set up your commands, options, and command handlers as usual...
    //
    var yourRootCommand = new RootCommand() ...
    ...

    if (args.Length == 0)
    {
        //
        // No commandline arguments given, output help text
        //
        var helpBld = new HelpBuilder(Resources.Instance, Console.WindowWidth);
        helpBld.Write(yourRootCommand, Console.Out);
        return;
    }

    yourRootCommand.Invoke(args);
}
kasajian

kasajian commented on Nov 4, 2021

@kasajian
Author

Thank you for this. Now on to an even more important question: How did you know this? There's nothing about this class in the docs, or am I looking in the wrong place?

There's other things I'm curious about such as -- for instance, what if I want to group the output of commands under headings like "Common" vs "Advanced" commands.

Also, what if I have 20 commands, and now I want to add a new global option, like "--settings-file", right now I'm having to update all 20 commands and passing new parameter "settingsFile", which works, but I would prefer handling that once and place the settings data in a global application context that each command can have access to.

elgonzo

elgonzo commented on Nov 4, 2021

@elgonzo
Contributor

How did you know this? There's nothing about this class in the docs, or am I looking in the wrong place?

As the saying goes, the documentation comments in the source code are the documentation ;-)

Some time ago already, when i was dealing with a similar desire to get the actual help text on demand regardless of the --help option, i roughly perused the documentation comments and the library source code, looking for methods / classes in the library which deal with help output. Once i noticed the HelpBuilder class, it was a very short way to get an understanding of how it can be used.

The nuget package for the library should also contain an (Intellisense) XML file containing the content of these documentation comments, which makes searching/perusing much easier than crawling just the source code. (The XML file is part of the unpacked nuget package that you should find in nuget's global-packages folder.)

As a side note, once the public API has stabilized with the non-beta 2.0 release someday in the not so far future (you can see the tracking for the 2.0 release milestone here: https://github.com/dotnet/command-line-api/milestone/1), i hope some love will be given to some proper API documentation :-)


Also, what if I have 20 commands, and now I want to add a new global option [...]

Look at the public methods offered by the Command class. It has the void AddGlobalOption(Option option) method for exactly that purpose. Also take a look at the documentation comments for this method:

/// <summary>
/// Adds a global <see cref="Option"/> to the command.
/// </summary>
/// <param name="option">The global option to add to the command.</param>
/// <remarks>Global options are applied to the command and recursively to subcommands. They do not apply to
/// parent commands.</remarks>
public void AddGlobalOption(Option option)

Keep in mind that you will have to have a matching argument in each of your command handlers that will receive the value of such a global option. On the other hand, if you use complex model bindings, you could put the properties for the global options in an abstract base class, and let your CLI model classes for each command inherit from this base class.

kasajian

kasajian commented on Nov 4, 2021

@kasajian
Author

I am already using AddGlobalOption for this. I add that to the root command, but then I have to pass the parameter in for each command.

But back to the HelpBuilder. What is Resources.Instance? The source says something about localization. I need to create a resource for localization to invoke the "/?" help? It already works if the user types "/?".. I just want to call the same thing programmatically (when the user enters no parameters)

And finally, when I do "F12" to view the source for the HelpBuilder constructor, I don't get a resource -- I get this:

        public HelpBuilder(IConsole console, int maxWidth = int.MaxValue)
        {
            Console = (console ?? throw new ArgumentNullException("console"));
            if (maxWidth <= 0)
            {
                maxWidth = int.MaxValue;
            }

            MaxWidth = maxWidth;
        }

So the version I have requires an IConsole.

Do I need to build everything from source to see where everything is at?

elgonzo

elgonzo commented on Nov 4, 2021

@elgonzo
Contributor

Ah, i forgot one of your questions. Sorry about that, here it is...

There's other things I'm curious about such as -- for instance, what if I want to group the output of commands under headings like "Common" vs "Advanced" commands.

Look at the public and protected methods offered by HelpBuilder, and you will notice this:

/// <summary>
///
/// </summary>
/// <param name="command">The command to get argument help items for.</param>
/// <param name="parseResult">A parse result providing context for help formatting.</param>
/// <param name="writer">The writer to write help output to.</param>
protected virtual void AddSubcommands(ICommand command, TextWriter writer, ParseResult parseResult)
{
var subcommands = GetSubcommands(command, parseResult).ToArray();
if (subcommands.Length > 0)
{
WriteHeading(LocalizationResources.HelpCommandsTitle(), null, writer);
RenderAsColumns(writer, subcommands);
writer.WriteLine();
}
}

So my suggestion would be to subclass HelpBuilder and override the AddSubCommands method. Your override would be mostly like the original AddSubCommands method, but with one distinction: it has to partion/split the gathered subcommands according to your grouping requirements, and call the RenderAsColumns method for each of those subcommand groups plus some additional output for whatever group headings you want/need...

elgonzo

elgonzo commented on Nov 4, 2021

@elgonzo
Contributor

But back to the HelpBuilder. What is Resources.Instance?

I noticed that recently (end of September) the name of this type has been changed from Resources to LocalizationResources. I didn't catch that when i wrote my first response to you. The source code of this type is here: https://github.com/dotnet/command-line-api/blob/8a0f746b0d9f2afba278349482cd7c2137de473d/src/System.CommandLine/LocalizationResources.cs

Basically, unless you want customize/localize the pre-defined text strings in the help text, use LocalizationResources.Instance. If you want/need to customize/localize (some of) the pre-defined text strings but do not want to do it by providing and/or altering the resource files themselves, create your own subclass derived from LocalizationResources and override the respective virtual methods as you see fit.

And finally, when I do "F12" to view the source for the HelpBuilder constructor, I don't get a resource -- I get this

So the version I have requires an IConsole.

Do I need to build everything from source to see where everything is at?

I would suggest to use one of the recent daily builds, and not rely on old library version. You don't need to build from souce yourself. There are nuget packages for the daily builds available. Further details about how to get these are in the readme.md of the project here: https://github.com/dotnet/command-line-api#packages

EricZimmerman

EricZimmerman commented on Dec 8, 2021

@EricZimmerman

since this seems to have changed, this is how it looks to work with the latest daily builds.

var helpBld = new HelpBuilder(LocalizationResources.Instance, Console.WindowWidth);
var hc = new HelpContext(helpBld,rootCommand,Console.Out);

helpBld.Write(hc);
skibitsky

skibitsky commented on Dec 17, 2021

@skibitsky

Please consider adding an answer to the How To document.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @jonsequitur@kasajian@EricZimmerman@elgonzo@skibitsky

        Issue actions

          How do you have your program show the Help menu when the user passes no command line arguments. · Issue #1449 · dotnet/command-line-api