Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-06 08:37:15

0001 // Copyright (c) 2017-2025, University of Cincinnati, developed by Henry Schreiner
0002 // under NSF AWARD 1414736 and by the respective contributors.
0003 // All rights reserved.
0004 //
0005 // SPDX-License-Identifier: BSD-3-Clause
0006 
0007 #pragma once
0008 
0009 // IWYU pragma: private, include "CLI/CLI.hpp"
0010 
0011 // This include is only needed for IDEs to discover symbols
0012 #include "../Formatter.hpp"
0013 
0014 // [CLI11:public_includes:set]
0015 #include <algorithm>
0016 #include <string>
0017 #include <utility>
0018 #include <vector>
0019 // [CLI11:public_includes:end]
0020 
0021 namespace CLI {
0022 // [CLI11:formatter_inl_hpp:verbatim]
0023 CLI11_INLINE std::string
0024 Formatter::make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const {
0025     std::stringstream out;
0026 
0027     out << "\n" << group << ":\n";
0028     for(const Option *opt : opts) {
0029         out << make_option(opt, is_positional);
0030     }
0031 
0032     return out.str();
0033 }
0034 
0035 CLI11_INLINE std::string Formatter::make_positionals(const App *app) const {
0036     std::vector<const Option *> opts =
0037         app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); });
0038 
0039     if(opts.empty())
0040         return {};
0041 
0042     return make_group(get_label("POSITIONALS"), true, opts);
0043 }
0044 
0045 CLI11_INLINE std::string Formatter::make_groups(const App *app, AppFormatMode mode) const {
0046     std::stringstream out;
0047     std::vector<std::string> groups = app->get_groups();
0048 
0049     // Options
0050     for(const std::string &group : groups) {
0051         std::vector<const Option *> opts = app->get_options([app, mode, &group](const Option *opt) {
0052             return opt->get_group() == group                     // Must be in the right group
0053                    && opt->nonpositional()                       // Must not be a positional
0054                    && (mode != AppFormatMode::Sub                // If mode is Sub, then
0055                        || (app->get_help_ptr() != opt            // Ignore help pointer
0056                            && app->get_help_all_ptr() != opt));  // Ignore help all pointer
0057         });
0058         if(!group.empty() && !opts.empty()) {
0059             out << make_group(group, false, opts);
0060 
0061             // Removed double newline between groups for consistency of help text
0062             // if(group != groups.back())
0063             //    out << "\n";
0064         }
0065     }
0066 
0067     return out.str();
0068 }
0069 
0070 CLI11_INLINE std::string Formatter::make_description(const App *app) const {
0071     std::string desc = app->get_description();
0072     auto min_options = app->get_require_option_min();
0073     auto max_options = app->get_require_option_max();
0074 
0075     if(app->get_required()) {
0076         desc += " " + get_label("REQUIRED") + " ";
0077     }
0078 
0079     if(min_options > 0) {
0080         if(max_options == min_options) {
0081             desc += " \n[Exactly " + std::to_string(min_options) + " of the following options are required]";
0082         } else if(max_options > 0) {
0083             desc += " \n[Between " + std::to_string(min_options) + " and " + std::to_string(max_options) +
0084                     " of the following options are required]";
0085         } else {
0086             desc += " \n[At least " + std::to_string(min_options) + " of the following options are required]";
0087         }
0088     } else if(max_options > 0) {
0089         desc += " \n[At most " + std::to_string(max_options) + " of the following options are allowed]";
0090     }
0091 
0092     return (!desc.empty()) ? desc + "\n\n" : std::string{};
0093 }
0094 
0095 CLI11_INLINE std::string Formatter::make_usage(const App *app, std::string name) const {
0096     std::string usage = app->get_usage();
0097     if(!usage.empty()) {
0098         return usage + "\n\n";
0099     }
0100 
0101     std::stringstream out;
0102     out << '\n';
0103 
0104     if(name.empty())
0105         out << get_label("Usage") << ':';
0106     else
0107         out << name;
0108 
0109     std::vector<std::string> groups = app->get_groups();
0110 
0111     // Print an Options badge if any options exist
0112     std::vector<const Option *> non_pos_options =
0113         app->get_options([](const Option *opt) { return opt->nonpositional(); });
0114     if(!non_pos_options.empty())
0115         out << " [" << get_label("OPTIONS") << "]";
0116 
0117     // Positionals need to be listed here
0118     std::vector<const Option *> positionals = app->get_options([](const Option *opt) { return opt->get_positional(); });
0119 
0120     // Print out positionals if any are left
0121     if(!positionals.empty()) {
0122         // Convert to help names
0123         std::vector<std::string> positional_names(positionals.size());
0124         std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [this](const Option *opt) {
0125             return make_option_usage(opt);
0126         });
0127 
0128         out << " " << detail::join(positional_names, " ");
0129     }
0130 
0131     // Add a marker if subcommands are expected or optional
0132     if(!app->get_subcommands(
0133                [](const CLI::App *subc) { return ((!subc->get_disabled()) && (!subc->get_name().empty())); })
0134             .empty()) {
0135         out << ' ' << (app->get_require_subcommand_min() == 0 ? "[" : "")
0136             << get_label(app->get_require_subcommand_max() == 1 ? "SUBCOMMAND" : "SUBCOMMANDS")
0137             << (app->get_require_subcommand_min() == 0 ? "]" : "");
0138     }
0139 
0140     out << "\n\n";
0141 
0142     return out.str();
0143 }
0144 
0145 CLI11_INLINE std::string Formatter::make_footer(const App *app) const {
0146     std::string footer = app->get_footer();
0147     if(footer.empty()) {
0148         return std::string{};
0149     }
0150     return '\n' + footer + '\n';
0151 }
0152 
0153 CLI11_INLINE std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const {
0154     // This immediately forwards to the make_expanded method. This is done this way so that subcommands can
0155     // have overridden formatters
0156     if(mode == AppFormatMode::Sub)
0157         return make_expanded(app, mode);
0158 
0159     std::stringstream out;
0160     if((app->get_name().empty()) && (app->get_parent() != nullptr)) {
0161         if(app->get_group() != "SUBCOMMANDS") {
0162             out << app->get_group() << ':';
0163         }
0164     }
0165     if(is_description_paragraph_formatting_enabled()) {
0166         detail::streamOutAsParagraph(
0167             out, make_description(app), description_paragraph_width_, "");  // Format description as paragraph
0168     } else {
0169         out << make_description(app) << '\n';
0170     }
0171     out << make_usage(app, name);
0172     out << make_positionals(app);
0173     out << make_groups(app, mode);
0174     out << make_subcommands(app, mode);
0175     std::string footer_string = make_footer(app);
0176 
0177     if(is_footer_paragraph_formatting_enabled()) {
0178         detail::streamOutAsParagraph(out, footer_string, footer_paragraph_width_);  // Format footer as paragraph
0179     } else {
0180         out << footer_string;
0181     }
0182 
0183     return out.str();
0184 }
0185 
0186 CLI11_INLINE std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const {
0187     std::stringstream out;
0188 
0189     std::vector<const App *> subcommands = app->get_subcommands({});
0190 
0191     // Make a list in definition order of the groups seen
0192     std::vector<std::string> subcmd_groups_seen;
0193     for(const App *com : subcommands) {
0194         if(com->get_name().empty()) {
0195             if(!com->get_group().empty() && com->get_group().front() != '+') {
0196                 out << make_expanded(com, mode);
0197             }
0198             continue;
0199         }
0200         std::string group_key = com->get_group();
0201         if(!group_key.empty() &&
0202            std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) {
0203                return detail::to_lower(a) == detail::to_lower(group_key);
0204            }) == subcmd_groups_seen.end())
0205             subcmd_groups_seen.push_back(group_key);
0206     }
0207 
0208     // For each group, filter out and print subcommands
0209     for(const std::string &group : subcmd_groups_seen) {
0210         out << '\n' << group << ":\n";
0211         std::vector<const App *> subcommands_group = app->get_subcommands(
0212             [&group](const App *sub_app) { return detail::to_lower(sub_app->get_group()) == detail::to_lower(group); });
0213         for(const App *new_com : subcommands_group) {
0214             if(new_com->get_name().empty())
0215                 continue;
0216             if(mode != AppFormatMode::All) {
0217                 out << make_subcommand(new_com);
0218             } else {
0219                 out << new_com->help(new_com->get_name(), AppFormatMode::Sub);
0220                 out << '\n';
0221             }
0222         }
0223     }
0224 
0225     return out.str();
0226 }
0227 
0228 CLI11_INLINE std::string Formatter::make_subcommand(const App *sub) const {
0229     std::stringstream out;
0230     std::string name = "  " + sub->get_display_name(true) + (sub->get_required() ? " " + get_label("REQUIRED") : "");
0231 
0232     out << std::setw(static_cast<int>(column_width_)) << std::left << name;
0233     detail::streamOutAsParagraph(
0234         out, sub->get_description(), right_column_width_, std::string(column_width_, ' '), true);
0235     out << '\n';
0236     return out.str();
0237 }
0238 
0239 CLI11_INLINE std::string Formatter::make_expanded(const App *sub, AppFormatMode mode) const {
0240     std::stringstream out;
0241     out << sub->get_display_name(true) << '\n';
0242 
0243     if(is_description_paragraph_formatting_enabled()) {
0244         detail::streamOutAsParagraph(
0245             out, make_description(sub), description_paragraph_width_, "  ");  // Format description as paragraph
0246     } else {
0247         out << make_description(sub) << '\n';
0248     }
0249 
0250     if(sub->get_name().empty() && !sub->get_aliases().empty()) {
0251         detail::format_aliases(out, sub->get_aliases(), column_width_ + 2);
0252     }
0253 
0254     out << make_positionals(sub);
0255     out << make_groups(sub, mode);
0256     out << make_subcommands(sub, mode);
0257     std::string footer_string = make_footer(sub);
0258 
0259     if(mode == AppFormatMode::Sub && !footer_string.empty()) {
0260         const auto *parent = sub->get_parent();
0261         std::string parent_footer = (parent != nullptr) ? make_footer(sub->get_parent()) : std::string{};
0262         if(footer_string == parent_footer) {
0263             footer_string = "";
0264         }
0265     }
0266     if(!footer_string.empty()) {
0267         if(is_footer_paragraph_formatting_enabled()) {
0268             detail::streamOutAsParagraph(out, footer_string, footer_paragraph_width_);  // Format footer as paragraph
0269         } else {
0270             out << footer_string;
0271         }
0272     }
0273     return out.str();
0274 }
0275 
0276 CLI11_INLINE std::string Formatter::make_option(const Option *opt, bool is_positional) const {
0277     std::stringstream out;
0278     if(is_positional) {
0279         const std::string left = "  " + make_option_name(opt, true) + make_option_opts(opt);
0280         const std::string desc = make_option_desc(opt);
0281         out << std::setw(static_cast<int>(column_width_)) << std::left << left;
0282 
0283         if(!desc.empty()) {
0284             bool skipFirstLinePrefix = true;
0285             if(left.length() >= column_width_) {
0286                 out << '\n';
0287                 skipFirstLinePrefix = false;
0288             }
0289             detail::streamOutAsParagraph(
0290                 out, desc, right_column_width_, std::string(column_width_, ' '), skipFirstLinePrefix);
0291         }
0292     } else {
0293         const std::string namesCombined = make_option_name(opt, false);
0294         const std::string opts = make_option_opts(opt);
0295         const std::string desc = make_option_desc(opt);
0296 
0297         // Split all names at comma and sort them into short names and long names
0298         const auto names = detail::split(namesCombined, ',');
0299         std::vector<std::string> vshortNames;
0300         std::vector<std::string> vlongNames;
0301         std::for_each(names.begin(), names.end(), [&vshortNames, &vlongNames](const std::string &name) {
0302             if(name.find("--", 0) != std::string::npos)
0303                 vlongNames.push_back(name);
0304             else
0305                 vshortNames.push_back(name);
0306         });
0307 
0308         // Assemble short and long names
0309         std::string shortNames = detail::join(vshortNames, ", ");
0310         std::string longNames = detail::join(vlongNames, ", ");
0311 
0312         // Calculate setw sizes
0313         // Short names take enough width to align long names at the desired ratio
0314         const auto shortNamesColumnWidth =
0315             static_cast<int>(static_cast<float>(column_width_) * long_option_alignment_ratio_);
0316         const auto longNamesColumnWidth = static_cast<int>(column_width_) - shortNamesColumnWidth;
0317         int shortNamesOverSize = 0;
0318 
0319         // Print short names
0320         if(!shortNames.empty()) {
0321             shortNames = "  " + shortNames;  // Indent
0322             if(longNames.empty() && !opts.empty())
0323                 shortNames += opts;  // Add opts if only short names and no long names
0324             if(!longNames.empty())
0325                 shortNames += ",";
0326             if(static_cast<int>(shortNames.length()) >= shortNamesColumnWidth) {
0327                 shortNames += " ";
0328                 shortNamesOverSize = static_cast<int>(shortNames.length()) - shortNamesColumnWidth;
0329             }
0330             out << std::setw(shortNamesColumnWidth) << std::left << shortNames;
0331         } else {
0332             out << std::setw(shortNamesColumnWidth) << std::left << "";
0333         }
0334 
0335         // Adjust long name column width in case of short names column reaching into long names column
0336         shortNamesOverSize =
0337             (std::min)(shortNamesOverSize, longNamesColumnWidth);  // Prevent negative result with unsigned integers
0338         const auto adjustedLongNamesColumnWidth = longNamesColumnWidth - shortNamesOverSize;
0339 
0340         // Print long names
0341         if(!longNames.empty()) {
0342             if(!opts.empty())
0343                 longNames += opts;
0344             if(static_cast<int>(longNames.length()) >= adjustedLongNamesColumnWidth)
0345                 longNames += " ";
0346 
0347             out << std::setw(adjustedLongNamesColumnWidth) << std::left << longNames;
0348         } else {
0349             out << std::setw(adjustedLongNamesColumnWidth) << std::left << "";
0350         }
0351 
0352         if(!desc.empty()) {
0353             bool skipFirstLinePrefix = true;
0354             if(out.str().length() > column_width_) {
0355                 out << '\n';
0356                 skipFirstLinePrefix = false;
0357             }
0358             detail::streamOutAsParagraph(
0359                 out, desc, right_column_width_, std::string(column_width_, ' '), skipFirstLinePrefix);
0360         }
0361     }
0362 
0363     out << '\n';
0364     return out.str();
0365 }
0366 
0367 CLI11_INLINE std::string Formatter::make_option_name(const Option *opt, bool is_positional) const {
0368     if(is_positional)
0369         return opt->get_name(true, false);
0370 
0371     return opt->get_name(false, true);
0372 }
0373 
0374 CLI11_INLINE std::string Formatter::make_option_opts(const Option *opt) const {
0375     std::stringstream out;
0376 
0377     if(!opt->get_option_text().empty()) {
0378         out << " " << opt->get_option_text();
0379     } else {
0380         if(opt->get_type_size() != 0) {
0381             if(!opt->get_type_name().empty())
0382                 out << " " << get_label(opt->get_type_name());
0383             if(!opt->get_default_str().empty())
0384                 out << " [" << opt->get_default_str() << "] ";
0385             if(opt->get_expected_max() == detail::expected_max_vector_size)
0386                 out << " ...";
0387             else if(opt->get_expected_min() > 1)
0388                 out << " x " << opt->get_expected();
0389 
0390             if(opt->get_required())
0391                 out << " " << get_label("REQUIRED");
0392         }
0393         if(!opt->get_envname().empty())
0394             out << " (" << get_label("Env") << ":" << opt->get_envname() << ")";
0395         if(!opt->get_needs().empty()) {
0396             out << " " << get_label("Needs") << ":";
0397             for(const Option *op : opt->get_needs())
0398                 out << " " << op->get_name();
0399         }
0400         if(!opt->get_excludes().empty()) {
0401             out << " " << get_label("Excludes") << ":";
0402             for(const Option *op : opt->get_excludes())
0403                 out << " " << op->get_name();
0404         }
0405     }
0406     return out.str();
0407 }
0408 
0409 CLI11_INLINE std::string Formatter::make_option_desc(const Option *opt) const { return opt->get_description(); }
0410 
0411 CLI11_INLINE std::string Formatter::make_option_usage(const Option *opt) const {
0412     // Note that these are positionals usages
0413     std::stringstream out;
0414     out << make_option_name(opt, true);
0415     if(opt->get_expected_max() >= detail::expected_max_vector_size)
0416         out << "...";
0417     else if(opt->get_expected_max() > 1)
0418         out << "(" << opt->get_expected() << "x)";
0419 
0420     return opt->get_required() ? out.str() : "[" + out.str() + "]";
0421 }
0422 // [CLI11:formatter_inl_hpp:end]
0423 }  // namespace CLI