Skip to content

Initial thoughts on generation #1829

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

Open
KathleenDollard opened this issue Aug 31, 2022 · 6 comments
Open

Initial thoughts on generation #1829

KathleenDollard opened this issue Aug 31, 2022 · 6 comments
Labels
Area-API enhancement New feature or request needs discussion Further discussion required

Comments

@KathleenDollard
Copy link
Contributor

@davidfowl

Here is the shape we are considering - very early thoughts.

An entry point in the System.CommandLine namespace or package namespace that is currently named ParserBuilder because an preliminary user study identified that some folks that are do not plan subcommands associate "Command" with that and think something named "Command" is the wrong choice. Also, argparse in Python makes folks with that background think parser.

To create the parser:

  • ParserBuilder.CreateFromMethod(<delegate as method group>) This evaluates the method, its attributes, parameter attributes and XmlComments to gather information for the command. You can also access the command after it is created. This is great for simple one level CLIs and would replace DragonFruit without using reflection.
  • ParserBuilder.CreateFromMethods(<tree, with node types to guide creation and delegates as method groups>) Multi-level CLIs only differ from the single level in that the programmer needs to define the relationship between the methods. This method explicitly does that.
  • ParserBuilder.CreateFromType(<type>) Some folks work on really big CLIs with more options than anyone would want to see as method parameters. This approach is very similar to CreateFromMethod, except it uses type properties and possibly fields.
  • ParserBuilder.CreateFromLambda(<delegate as lambda>) Similar to the above (although generation may be more problematic in imperfect code)
  • ParserBuilder.Create() With this approach, the user would explicitly add subcommands, options and arguments. There is more flexibility in the code that is run with this approach.

The resulting parser is a wrapper for System.CommandLine.Command and offers strongly typed access to the tree. IOW, this is valid for a StarTrek parser:

var janewayOption = pb.NextGeneration.Voyager.Janeway;

We are still looking at the relationship between parsing and invocation. System.CommandLine supplies a bunch of features via this pipeline (most things except validation). The simple case is very simple:

var pb = ParserBuilder.CreateFromMethod(Greet);   // Greet is a method with descriptions in XML comments
return pb.Invoke();  // If args are not specified Environment.CommandLineArgs is used. 

Note that this and the Greet method is the entire application.

Finding args in Top level statement apps not obvious, thus the default to Environment... When folks are using old-style apps, they will want to know where to put the args, so that overload also exists.

If you just want to get the results, and then switch on them, strongly typed results are provided via GetResults.

If you are interested in customizing the pipeline (including removing things from it) I would like to know the scenario to plan how much we balance making this easy vs exposing the metal.

What do you want to make sure you can easily do?

@KalleOlaviNiemitalo
Copy link

Will the generated code work in a C# project that has <LangVersion>7.3</LangVersion>, which is the last version supported on .NET Framework?

@KathleenDollard
Copy link
Contributor Author

I hope the answer to that is Yes. If System.CommandLine supports it, I hope it will, although the current prototypes do not. Thank for the reminder on the importance of this.

@KathleenDollard
Copy link
Contributor Author

I want to work with folks that have more options/arguments than are comfortable as method parameters to design the CreateFromType approach and understand what level of generation you want in those scenarios.

  • I assume the type would be the strongly typed result
  • Would you be OK with generated code setting the values (mapping)
  • Would you want routing (what method to run with the result)
  • Are you doing special things like custom parsing or just descriptions and aliases. Where you need to add things, would it work to annotate your type, or is that a domain type that should have no S.CL artifacts
  • What pitfalls (or why would you be skeptical if you would be skeptical)

@KalleOlaviNiemitalo
Copy link

KalleOlaviNiemitalo commented Aug 31, 2022

Custom completions that depend on parse results. I implemented a Command with Argument<FileInfo[]> and Option<string[]>. The completion callback of the option gets the value of the argument and generates completions depending on the contents of those files.

Those completions are CompletionItem instances rather than strings, so the completion code is already specific to System.CommandLine and annotations would not be a problem.

@KalleOlaviNiemitalo
Copy link

How is localisation going to work with XML comments?

@KathleenDollard
Copy link
Contributor Author

@KalleOlaviNiemitalo it depends on the approach to localization. If it is based on the default language's string, then it would be quite easy to generate

Description = Localized(<xml comment description>);

The challenge would be hooking up to the localization approach the user wanted. I am quite out of date on my understanding of common practices here, and would tend to mimic ASP.NET if possible.

@jonsequitur jonsequitur added needs discussion Further discussion required enhancement New feature or request Area-API labels Sep 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-API enhancement New feature or request needs discussion Further discussion required
Projects
None yet
Development

No branches or pull requests

3 participants