-
Notifications
You must be signed in to change notification settings - Fork 383
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
Apply help to all symbols in the CLI declaration #2442
base: main-powderhouse
Are you sure you want to change the base?
Apply help to all symbols in the CLI declaration #2442
Conversation
@dotnet-policy-service agree |
static void AddOptionRecursively(CliCommand command, CliOption option) | ||
{ | ||
command.Add(option); | ||
|
||
foreach (var subcommand in command.Subcommands) | ||
{ | ||
AddOptionRecursively(subcommand, option); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I imagine this method should belong to CliCommand or some extensions method, but I didn't want to change too much in this PR without consulting you first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am torn between having this be an extension method on CliOption or CliCommand. Regardless, I think it belongs in the core (System.CommandLine) and appreciate you being conservative on this. Let's get some more thoughts on the location.
I think we should do a check so that if it is added twice we don't have problems. I think we should do that by name. We are in process of adding a GetSymbolName
method, although I need to check that it is available on a CliCommand
.
We will need to decide if first or last should win. My first intuition is to have the first win, so calling this method is "add the option to any commands that do not already have an option by this name". If you wanted custom help on one option (or another option), I think it would be most natural to add that first. Similarly, if you wanted one branch of commands in a complex tree to do help differently (dotnet new
for example), you'd just have to call in order most specialized to least.
The other thing nagging on this is that our previous approach is that the CLI tree needs to be created before this is called. Another approach (although I like it less) is to mark the command and add the option to each command immediately before parsing. I am concerned about bad behavior if the tree is reused and a different help system is set (unless we copy the tree).
Sorry for not having a clear answer today on this. It's summer and we have some folks out, so it may be a bit before we have a clear answer on where and how to do the check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy to let it wait for the right time and update the code whenever the questions you pointed out have answers. Thank you sharing all the context, I do appreciate it very much!
I am torn between having this be an extension method on CliOption or CliCommand. Regardless, I think it belongs in the core (System.CommandLine) and appreciate you being conservative on this. Let's get some more thoughts on the location.
My personal taste would be CliCommand
, but I'm unsure as I know my pov is most likely restricted to my use-cases and understand that every decision impacts a huge audience.
I think we should do a check so that if it is added twice we don't have problems. I think we should do that by name. We are in process of adding a GetSymbolName method, although I need to check that it is available on a CliCommand.
Looking around on main-powderhouse
I can see there's a GetSymbolByName
in ParseResult
class, but unfrotunately not available on CliCommand
. Are you planning on bringing it or should I try to access it with some workaround?
We will need to decide if first or last should win. My first intuition is to have the first win, so calling this method is "add the option to any commands that do not already have an option by this name". If you wanted custom help on one option (or another option), I think it would be most natural to add that first. Similarly, if you wanted one branch of commands in a complex tree to do help differently (dotnet new for example), you'd just have to call in order most specialized to least.
Haven't really thought about it before, but I'd say your first intuition makes a lot of sense to me.
The other thing nagging on this is that our previous approach is that the CLI tree needs to be created before this is called. Another approach (although I like it less) is to mark the command and add the option to each command immediately before parsing. I am concerned about bad behavior if the tree is reused and a different help system is set (unless we copy the tree).
I believe there's much smarter individuals to discuss this with, but my intuition would be to stick with 'frozen' CLI tree - it just sounds right, unless there's any good reason not to use it. Is there any appropriate plugging point to add help 'higher' in the chain? Whatever the decision will be, do let me know - I'd love to have my small addition in the .NET ecosystem :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this approach makes sense, thanks for submitting the PR! I left one clarifying question on one of the tests.
[Fact] | ||
public void Custom_help_subsystem_can_be_used() | ||
{ | ||
var consoleHack = new ConsoleHack().RedirectToBuffer(true); | ||
var pipeline = Pipeline.CreateEmpty(); | ||
pipeline.Help = new AlternateSubsystems.AlternateHelp(); | ||
|
||
pipeline.Execute(new CliConfiguration(new CliRootCommand()), "-h", consoleHack); | ||
consoleHack.GetBuffer().Trim().Should().Be($"***Help me!***"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be misssing something but it seems like Custom_help_subsystem_can_be_used
and Custom_help_subsystem_can _replace_standard
are the same test. Is this the case? If so I think we might want to consolidate them
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've copied these shamelessly from VersionSubsystemTests.cs
Couldn't really tell why there are two tests with identical body there, but I assumed I don't understand something and chose to add it. Removing it in the upcoming commit. Should I also remove the duplicate tests from VersionSubsystemTests.cs
- specifically Custom_version_subsystem_can_replace_standard
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Welcome! Thanks for this and including great tests!
In the long comment, this opens a couple of design questions and summer absences may result in a delay, but we should definitely be able to take this with tweaks.
static void AddOptionRecursively(CliCommand command, CliOption option) | ||
{ | ||
command.Add(option); | ||
|
||
foreach (var subcommand in command.Subcommands) | ||
{ | ||
AddOptionRecursively(subcommand, option); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am torn between having this be an extension method on CliOption or CliCommand. Regardless, I think it belongs in the core (System.CommandLine) and appreciate you being conservative on this. Let's get some more thoughts on the location.
I think we should do a check so that if it is added twice we don't have problems. I think we should do that by name. We are in process of adding a GetSymbolName
method, although I need to check that it is available on a CliCommand
.
We will need to decide if first or last should win. My first intuition is to have the first win, so calling this method is "add the option to any commands that do not already have an option by this name". If you wanted custom help on one option (or another option), I think it would be most natural to add that first. Similarly, if you wanted one branch of commands in a complex tree to do help differently (dotnet new
for example), you'd just have to call in order most specialized to least.
The other thing nagging on this is that our previous approach is that the CLI tree needs to be created before this is called. Another approach (although I like it less) is to mark the command and add the option to each command immediately before parsing. I am concerned about bad behavior if the tree is reused and a different help system is set (unless we copy the tree).
Sorry for not having a clear answer today on this. It's summer and we have some folks out, so it may be a bit before we have a clear answer on where and how to do the check.
var result = pipeline.Parse(configuration, ""); | ||
|
||
rootCommand.Options.Should().NotBeNull(); | ||
rootCommand.Options |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a SymbolByName
method in flight and need to ensure that it will work in this context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to know! Will update the test when it becomes available on the CliCommand
or should I try to grab the help option from ParseResult
?
Value declared as non-null Co-authored-by: Kevin B <[email protected]>
Co-authored-by: Kevin B <[email protected]>
Value declared as non-null Co-authored-by: Kevin B <[email protected]>
Co-authored-by: Kevin B <[email protected]>
Co-authored-by: Kevin B <[email protected]>
This PR aims to address #2343 by adding help options to alll comands in the tree and includes tests.
Would love any thoughts or feedback as this is my first open-source PR.