Skip to content
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

Open
wants to merge 11 commits into
base: main-powderhouse
Choose a base branch
from

Conversation

hasnik
Copy link

@hasnik hasnik commented Jun 13, 2024

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.

@hasnik
Copy link
Author

hasnik commented Jun 13, 2024

@dotnet-policy-service agree

Comment on lines +31 to +39
static void AddOptionRecursively(CliCommand command, CliOption option)
{
command.Add(option);

foreach (var subcommand in command.Subcommands)
{
AddOptionRecursively(subcommand, option);
}
}
Copy link
Author

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.

Copy link
Contributor

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.

Copy link
Author

@hasnik hasnik Jun 18, 2024

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 :)

@KathleenDollard KathleenDollard added the Powderhouse Work to isolate parser and features label Jun 17, 2024
Copy link

@JeanJoeris JeanJoeris left a 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.

Comment on lines 67 to 75
[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!***");
Copy link

@JeanJoeris JeanJoeris Jun 18, 2024

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

Copy link
Author

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?

Copy link
Contributor

@KathleenDollard KathleenDollard left a 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.

src/System.CommandLine.Subsystems.Tests/TestData.cs Outdated Show resolved Hide resolved
Comment on lines +31 to +39
static void AddOptionRecursively(CliCommand command, CliOption option)
{
command.Add(option);

foreach (var subcommand in command.Subcommands)
{
AddOptionRecursively(subcommand, option);
}
}
Copy link
Contributor

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
Copy link
Contributor

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.

Copy link
Author

@hasnik hasnik Jun 18, 2024

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Powderhouse Work to isolate parser and features
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants