commandline icon indicating copy to clipboard operation
commandline copied to clipboard

After customizing help generation, hep VERB does not work

Open p0wertiger opened this issue 4 years ago • 16 comments

Did this

static async Task Main( string[] args )
{
    _logger.Info(CommandLine.Text.HeadingInfo.Default);

    var parser = new Parser(p => { p.AutoVersion = false; p.CaseInsensitiveEnumValues = true;  });
    var parserResult = parser.ParseArguments<SyncOptions, QueryOptions, TokenOptions>(args);
    await parserResult.MapResult(
            (SyncOptions opts) => SyncAsync(opts),
            (QueryOptions opts) => QueryAsync(opts),
            (TokenOptions opts) => ShowTokenAsync(opts),
            errors => Task.FromResult(DisplayHelp(parserResult, errors))
        )
        .ContinueWith(t => {
            if ( System.Diagnostics.Debugger.IsAttached )
            {
                Console.WriteLine("Press enter...");
                Console.ReadLine();
            }
        });
}

private static int DisplayHelp<T>( ParserResult<T> result, IEnumerable<Error> errs )
{
    var helpText = CommandLine.Text.HelpText.AutoBuild(result, h =>
    {
        h.AdditionalNewLineAfterOption = false;
        h.AddNewLineBetweenHelpSections = true;
        h.AddEnumValuesToHelpText = true;
        h.AutoVersion = false;
        h.Heading = CommandLine.Text.HeadingInfo.Empty;
        h.MaximumDisplayWidth = 110;
        h.AddVerbs(typeof(SyncOptions), typeof(QueryOptions), typeof(TokenOptions));
        return CommandLine.Text.HelpText.DefaultParsingErrorsHandler(result, h);
    }, e => e,
    true);
    Console.WriteLine(helpText);
    return 1;
}

Before this I could type MyProgram help VERB and it would tell me the same as MyProgram VERB minus errors section. Now this gives me only verb list, like if help lost its first argument. This does not work either: MyProgram VERB help but this does: MyProgram VERB --help.

Am I doing something wrong here?

p0wertiger avatar May 20 '20 17:05 p0wertiger

starting v2.7+, there is a new simplified overload method:

 private static int DisplayHelp<T>(ParserResult<T> result, IEnumerable<Error> errs)
        {
            var helpText = CommandLine.Text.HelpText.AutoBuild(result, h =>
            {
                h.AdditionalNewLineAfterOption = false;
                h.AddNewLineBetweenHelpSections = true;
                h.AddEnumValuesToHelpText = true;
                h.AutoVersion = false;
                h.Heading = CommandLine.Text.HeadingInfo.Empty;
                h.MaximumDisplayWidth = 110;
                //  h.AddVerbs(typeof(SyncOptions), typeof(QueryOptions), typeof(TokenOptions));
                return h;  // only h
            });
            Console.WriteLine(helpText);
            return 1;
        }

Let me know if this working with you

moh-hassan avatar May 22 '20 23:05 moh-hassan

I was trying one of the examples on the FAQ section ([https://dotnetfiddle.net/IbwLbX]) and it seems that the help doesn't work for verbs. I was using the following list of options

new string[] { "help" }
			,new string[] { "help clone" }
			,new string[] { "help commit" }
			,new string[] { "commit help" }
			,new string[] { "clone help" }
			,new string[] { "help clone" }
			,new string[] { "clone -help" }
			,new string[] { "clone --help" }
			,new string[] { "clone -?" }
			,new string[] { "?" }

But all of them fails except for the first one. Which of them should work?

Since On .netfiddler I can't see the Error output, I modified the __YourMain method as follows

	static int __YourMain(string[] args)
	{
		int exitcode;
		var helpWriter = new StringWriter ();
 		var parser = new CommandLine.Parser (with => with.HelpWriter = helpWriter);
		
		exitcode = parser.ParseArguments<CommitOptions,CloneOptions>(args)
			.MapResult(
			(CommitOptions opts) => RunCommitCommand(opts),
			(CloneOptions opts) => RunCloneCommand(opts), 
			//(AddOptions opts) => RunAddCommand(opts), 
			(parserErrors) => DisplayHelp (parserErrors, helpWriter)
		);
		//this works because there's a Dummy verb "NextVerbOptions" class
		//exitcode = CommandLine.Parser.Default
		//	.ParseArguments<CommitOptions,CloneOptions>(args)
		//	.MapResult(
		//	(CommitOptions opts) => RunCommitCommand(opts),
		//	(CloneOptions opts) => RunCloneCommand(opts), 
		//	//(AddOptions opts) => RunAddCommand(opts), 
		//	(parserErrors) => 1
		//);
		
		return exitcode;
	}
	

 	static int DisplayHelp (IEnumerable<Error> errs, TextWriter helpWriter) {
 		//if (errs.IsVersion () || errs.IsHelp ())
 			Console.WriteLine (helpWriter.ToString ());
 		//else
 		//	Console.Error.WriteLine (helpWriter.ToString ());
		return 1;
 	}

I'm doing something wrong or I'm missing something here?

DkAngelito avatar Jun 01 '20 05:06 DkAngelito

@DkAngelito Just modify args as:

new string[] { "--help" } // --help,  show help for non verb optons
,new string[] { "clone", "--help" } // clone --help , show help for clone verb
,new string[] { "clone", "git://github.com/commandlineparser/commandline.git" } 
,new string[] { "add","--help" }  //add --help , show help for add verb
,new string[] { "add", "addfile.cs" }
,new string[] { "commit", "-m", "initial commit" }
,new string[] { "push", "-f" }
,new string[] { "push", "--force" }
,new string[] { "push" }

The concatenated args is the actuall command line. Wiki will be modified to a new modified version

moh-hassan avatar Jun 02 '20 06:06 moh-hassan

I don't understand the above instructions. My heavily-cut adjustment from the example can be found here (My actual code is using a set of verb classes, but the setup here is the same)

What do I "modify args as"?

Tsaukpaetra avatar Jun 12 '20 07:06 Tsaukpaetra

My actual code is using a set of verb classes, but the setup here is the same

You should add VerbAttribute to your classes like:

[Verb("myverb")] //whatever name, here  verb is named 'myverb'
class Options
{
...
}

To display help for verbs use this syntax:

<MyVerbName> --help
# for the above verb, type:
myverb --help

I added a comment to explain args[] I modified your code here and adding verb attribute, and it's working fine.

moh-hassan avatar Jun 12 '20 08:06 moh-hassan

Hmm. I was trying to use the help auto-verb, is that not possible with a custom help text?

Tsaukpaetra avatar Jun 12 '20 14:06 Tsaukpaetra

You can not customize auto generated help.

moh-hassan avatar Jun 12 '20 15:06 moh-hassan

I don't want that, I want there to be the implicit "help" verb that uses the positional argument 0 as the verb to get help with.

i.e. help myverb gets auto translated to myverb --help

Do I just have to make this myself?

Tsaukpaetra avatar Jun 12 '20 16:06 Tsaukpaetra

I don't want that, I want there to be the implicit "help" verb that uses the positional argument 0 as the verb to get help with.

i.e. help myverb gets auto translated to myverb --help

Do I just have to make this myself?

That works by default you could try it

in this example from @moh-hassan new string[] { "clone", "--help" }

would be equivalent to new string[] { "help", "clone" }

DkAngelito avatar Jun 12 '20 17:06 DkAngelito

That's what I'm saying is not working. Adjusting my example here, it just shows... nothing. Presumably because there is not help verb.

Tsaukpaetra avatar Jun 12 '20 17:06 Tsaukpaetra

Upgrade your example to the latest version (2.8+) and change your helptext class for the following and it should work

var helpText = HelpText.AutoBuild(result, h =>
{
	h.AdditionalNewLineAfterOption = false; //remove the extra newline between options
	h.Heading = "Myapp 2.0.0-beta"; //change header
	h.Copyright = "Copyright (c) 2019 Global.com"; //change copyrigt text
	return HelpText.DefaultParsingErrorsHandler(result, h);
}
//		, e => e); Remove this line 
); // add this one

DkAngelito avatar Jun 13 '20 02:06 DkAngelito

That worked!

Just gotta get the errors lines to stop repeating and I'm golden...

Tsaukpaetra avatar Jun 13 '20 03:06 Tsaukpaetra

Hi @Tsaukpaetra

You need to change the return HelpText.DefaultParsingErrorsHandler(result, h); to just return h; as the AutoBuild will already call this method internally.

JosKrause avatar Sep 20 '20 15:09 JosKrause

@DkAngelito Thank You!

// , e => e); Remove this line

This one line was what was throwing me off and I was banging my head on my keyboard. The overload with this should be marked obsolete and/or killed with fire. What a headache!

sflanker avatar Nov 01 '20 11:11 sflanker

Did this

static async Task Main( string[] args )
{
    _logger.Info(CommandLine.Text.HeadingInfo.Default);

    var parser = new Parser(p => { p.AutoVersion = false; p.CaseInsensitiveEnumValues = true;  });
    var parserResult = parser.ParseArguments<SyncOptions, QueryOptions, TokenOptions>(args);
    await parserResult.MapResult(
            (SyncOptions opts) => SyncAsync(opts),
            (QueryOptions opts) => QueryAsync(opts),
            (TokenOptions opts) => ShowTokenAsync(opts),
            errors => Task.FromResult(DisplayHelp(parserResult, errors))
        )
        .ContinueWith(t => {
            if ( System.Diagnostics.Debugger.IsAttached )
            {
                Console.WriteLine("Press enter...");
                Console.ReadLine();
            }
        });
}

How you got that async to work? I cannot see any async/await support at all.

mcflux avatar Jun 25 '21 13:06 mcflux

Did this:

static void Main(string[] args)
{
    Parser.Default
        .ParseArguments<ListItemsOptions, AddItemOptions>(args)
        .WithParsed(Run<ListItemsOptions>(ListItems))
        .WithParsed(Run<AddItemOptions>(AddItem));
}

private static Action<T> Run<T>(Func<T, Task> asyncAction) =>
    (T item) => asyncAction(item).GetAwaiter().GetResult();

private static async Task AddItem(AddItemOptions args)
{
    // do some async stuf...
}

private static async Task ListItems(ListItemsOptions args)
{
    // do more async stuf...
}

nkoudelia avatar Dec 10 '21 10:12 nkoudelia