diff --git a/common/command_line.cpp b/common/command_line.cpp index b42accabe054..6f7efd0de234 100644 --- a/common/command_line.cpp +++ b/common/command_line.cpp @@ -92,6 +92,8 @@ class MetaPrinter { void RegisterWithCommand(const Command& command, CommandBuilder& builder); void PrintHelp(const Command& command) const; + void PrintHelpForSubcommandName(const Command& command, + llvm::StringRef subcommand_name) const; void PrintVersion(const Command& command) const; void PrintSubcommands(const Command& command) const; @@ -153,6 +155,13 @@ line usage, and details of each option that can be provided. .help_short = SubHelpCommandInfo.help_short, }; + static constexpr ArgInfo HelpSubcommandArgInfo = { + .name = "subcommand", + .help = R"""( +Which subcommand to print help information for. +)""", + }; + static constexpr CommandInfo VersionCommandInfo = { .name = "version", .help = R"""( @@ -198,6 +207,9 @@ Prints the version of this command. // A flag that may be configured during command line parsing to select between // long and short form help output. bool short_help_ = false; + + // The requested subcommand to print help information for. + llvm::StringRef help_subcommand_; }; void MetaPrinter::RegisterWithCommand(const Command& command, @@ -212,7 +224,16 @@ void MetaPrinter::RegisterWithCommand(const Command& command, builder.AddSubcommand( is_subcommand ? SubHelpCommandInfo : HelpCommandInfo, [&](CommandBuilder& sub_b) { - sub_b.Meta([this, &command]() { PrintHelp(command); }); + sub_b.AddStringPositionalArg(HelpSubcommandArgInfo, [&](auto& arg_b) { + arg_b.Set(&help_subcommand_); + }); + sub_b.Meta([this, &command]() { + if (help_subcommand_.empty()) { + PrintHelp(command); + } else { + PrintHelpForSubcommandName(command, help_subcommand_); + } + }); }); // Only add version printing support if there is a version string @@ -280,6 +301,19 @@ void MetaPrinter::PrintHelp(const Command& command) const { out_ << "\n"; } +void MetaPrinter::PrintHelpForSubcommandName( + const Command& command, llvm::StringRef subcommand_name) const { + for (const auto& subcommand : command.subcommands) { + if (subcommand->info.name == subcommand_name) { + PrintHelp(*subcommand); + return; + } + } + + out_ << "ERROR: Could not find a subcommand named '" << subcommand_name + << "'.\n"; +} + void MetaPrinter::PrintVersion(const Command& command) const { CARBON_CHECK(!command.info.version.empty()) << "Printing should not be enabled without a version string configured."; diff --git a/common/command_line_test.cpp b/common/command_line_test.cpp index b5efde1c8131..ff0e4115f5f5 100644 --- a/common/command_line_test.cpp +++ b/common/command_line_test.cpp @@ -737,6 +737,7 @@ A hidden subcommand. }; TestRawOstream os; + EXPECT_THAT(parse({"--flag", "--help"}, os), Eq(ParseResult::MetaSuccess)); std::string help_flag_output = os.TakeStr(); EXPECT_THAT(help_flag_output, StrEq(llvm::StringRef(R"""( @@ -796,7 +797,8 @@ Build config: test-config-info EXPECT_THAT(parse({"--flag", "edit", "--option=42", "--help"}, os), Eq(ParseResult::MetaSuccess)); - EXPECT_THAT(os.TakeStr(), StrEq(llvm::StringRef(R"""( + std::string edit_help_output = os.TakeStr(); + EXPECT_THAT(edit_help_output, StrEq(llvm::StringRef(R"""( Edit the widget. This will take the provided widgets and edit them. @@ -818,11 +820,15 @@ Subcommand 'edit' options: That's all. )""") - .ltrim('\n'))); + .ltrim('\n'))); + + EXPECT_THAT(parse({"help", "edit"}, os), Eq(ParseResult::MetaSuccess)); + EXPECT_THAT(os.TakeStr(), StrEq(edit_help_output)); EXPECT_THAT(parse({"--flag", "run", "--option=abc", "--help"}, os), Eq(ParseResult::MetaSuccess)); - EXPECT_THAT(os.TakeStr(), StrEq(llvm::StringRef(R"""( + std::string run_help_output = os.TakeStr(); + EXPECT_THAT(run_help_output, StrEq(llvm::StringRef(R"""( Run wombats across the screen. This will cause several wombats to run across your screen. @@ -852,7 +858,10 @@ Subcommand 'run' options: Or it won't, who knows. )""") - .ltrim('\n'))); + .ltrim('\n'))); + + EXPECT_THAT(parse({"help", "run"}, os), Eq(ParseResult::MetaSuccess)); + EXPECT_THAT(os.TakeStr(), StrEq(run_help_output)); } TEST(ArgParserTest, HelpMarkdownLike) {