@@ -553,7 +553,7 @@ void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strV
553553 m_settings.forced_settings [SettingName (strArg)] = strValue;
554554}
555555
556- void ArgsManager::AddCommand (const std::string& cmd, const std::string& help)
556+ void ArgsManager::AddCommand (const std::string& cmd, const std::string& help, std::set<std::string>&& options )
557557{
558558 Assert (cmd.find (' =' ) == std::string::npos);
559559 Assert (cmd.at (0 ) != ' -' );
@@ -562,6 +562,9 @@ void ArgsManager::AddCommand(const std::string& cmd, const std::string& help)
562562 m_accept_any_command = false ; // latch to false
563563 std::map<std::string, Arg>& arg_map = m_available_args[OptionsCategory::COMMANDS];
564564 auto ret = arg_map.emplace (cmd, Arg{" " , help, ArgsManager::COMMAND});
565+ if (!options.empty ()) {
566+ m_command_args.try_emplace (cmd, std::move (options));
567+ }
565568 Assert (ret.second ); // Fail on duplicate commands
566569}
567570
@@ -618,14 +621,46 @@ void ArgsManager::CheckMultipleCLIArgs() const
618621 }
619622}
620623
624+ namespace {
625+ /* * Helper class for iterating over COMMAND_OPTIONS applicable to a given command */
626+ template <typename T>
627+ class CommandOptionsGetter
628+ {
629+ private:
630+ const typename T::const_iterator m_end;
631+ const typename T::const_iterator m_iter;
632+ public:
633+ CommandOptionsGetter (const T& available_args)
634+ : m_end{available_args.end ()},
635+ m_iter{available_args.find (OptionsCategory::COMMAND_OPTIONS)}
636+ {
637+ }
638+
639+ template <typename Fn>
640+ void Iterate (const std::set<std::string>& select, bool with_debug, Fn&& fn) const
641+ {
642+ if (select.empty ()) return ;
643+ if (m_iter == m_end) return ;
644+ for (const auto & [cmdopt_name, cmdopt_info] : m_iter->second ) {
645+ if (!with_debug && (cmdopt_info.m_flags & ArgsManager::DEBUG_ONLY)) continue ;
646+ if (!select.contains (cmdopt_name)) continue ;
647+ fn (cmdopt_name, cmdopt_info);
648+ }
649+ }
650+ };
651+ } // anonymous namespace
652+
621653std::string ArgsManager::GetHelpMessage () const
622654{
623655 const bool show_debug = GetBoolArg (" -help-debug" , false );
624656
625657 std::string usage;
626658 LOCK (cs_args);
627- for (const auto & arg_map : m_available_args) {
628- switch (arg_map.first ) {
659+
660+ const auto command_options = CommandOptionsGetter (m_available_args);
661+
662+ for (const auto & [category, category_args] : m_available_args) {
663+ switch (category) {
629664 case OptionsCategory::OPTIONS:
630665 usage += HelpMessageGroup (" Options:" );
631666 break ;
@@ -671,22 +706,29 @@ std::string ArgsManager::GetHelpMessage() const
671706 case OptionsCategory::CLI_COMMANDS:
672707 usage += HelpMessageGroup (" CLI Commands:" );
673708 break ;
709+ case OptionsCategory::COMMAND_OPTIONS:
710+ break ;
674711 default :
675712 break ;
676713 }
677714
715+ if (category == OptionsCategory::COMMAND_OPTIONS) continue ;
716+
678717 // When we get to the hidden options, stop
679- if (arg_map.first == OptionsCategory::HIDDEN) break ;
680-
681- for (const auto & arg : arg_map.second ) {
682- if (show_debug || !(arg.second .m_flags & ArgsManager::DEBUG_ONLY)) {
683- std::string name;
684- if (arg.second .m_help_param .empty ()) {
685- name = arg.first ;
686- } else {
687- name = arg.first + arg.second .m_help_param ;
718+ if (category == OptionsCategory::HIDDEN) break ;
719+
720+ for (const auto & [arg_name, arg_info] : category_args) {
721+ if (show_debug || !(arg_info.m_flags & ArgsManager::DEBUG_ONLY)) {
722+ usage += HelpMessageOpt (arg_name, arg_info.m_help_param , arg_info.m_help_text );
723+
724+ if (category == OptionsCategory::COMMANDS) {
725+ const auto cmd_args = m_command_args.find (arg_name);
726+ if (cmd_args != m_command_args.end ()) {
727+ command_options.Iterate (cmd_args->second , show_debug, [&](const auto & cmdopt_name, const auto & cmdopt_info) {
728+ usage += HelpMessageSubOpt (cmdopt_name, cmdopt_info.m_help_param , cmdopt_info.m_help_text );
729+ });
730+ }
688731 }
689- usage += HelpMessageOpt (name, arg.second .m_help_text );
690732 }
691733 }
692734 }
@@ -712,11 +754,11 @@ std::string HelpMessageGroup(const std::string &message) {
712754 return std::string (message) + std::string (" \n\n " );
713755}
714756
715- std::string HelpMessageOpt (const std::string & option, const std::string & message) {
716- return std::string (optIndent, ' ' ) + std::string (option) +
717- std::string ( " \n " ) + std::string (msgIndent, ' ' ) +
718- FormatParagraph (message, screenWidth - msgIndent, msgIndent) +
719- std::string ( " \n\n " );
757+ std::string HelpMessageOpt (std::string_view option, std::string_view help_param, std::string_view message, int indent)
758+ {
759+ return strprintf ( " %*s%s%s \n %*s%s \n\n " ,
760+ (optIndent + indent), " " , option, help_param,
761+ (msgIndent + indent), " " , FormatParagraph (message, screenWidth - msgIndent - indent, msgIndent + indent) );
720762}
721763
722764const std::vector<std::string> TEST_OPTIONS_DOC{
@@ -733,6 +775,11 @@ bool HasTestOption(const ArgsManager& args, const std::string& test_option)
733775 });
734776}
735777
778+ std::string HelpMessageSubOpt (std::string_view option, std::string_view help_param, std::string_view message)
779+ {
780+ return HelpMessageOpt (option, help_param, message, msgIndent - optIndent);
781+ }
782+
736783fs::path GetDefaultDataDir ()
737784{
738785 // Windows:
0 commit comments