Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-01-08 10:09:17

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() < 2 || app->get_require_subcommand_min() > 1 ? "SUBCOMMAND"
0137                                                                                                         : "SUBCOMMANDS")
0138             << (app->get_require_subcommand_min() == 0 ? "]" : "");
0139     }
0140 
0141     out << "\n\n";
0142 
0143     return out.str();
0144 }
0145 
0146 CLI11_INLINE std::string Formatter::make_footer(const App *app) const {
0147     std::string footer = app->get_footer();
0148     if(footer.empty()) {
0149         return std::string{};
0150     }
0151     return '\n' + footer + "\n\n";
0152 }
0153 
0154 CLI11_INLINE std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const {
0155     // This immediately forwards to the make_expanded method. This is done this way so that subcommands can
0156     // have overridden formatters
0157     if(mode == AppFormatMode::Sub)
0158         return make_expanded(app, mode);
0159 
0160     std::stringstream out;
0161     if((app->get_name().empty()) && (app->get_parent() != nullptr)) {
0162         if(app->get_group() != "SUBCOMMANDS") {
0163             out << app->get_group() << ':';
0164         }
0165     }
0166 
0167     detail::streamOutAsParagraph(
0168         out, make_description(app), description_paragraph_width_, "");  // Format description as paragraph
0169     out << make_usage(app, name);
0170     out << make_positionals(app);
0171     out << make_groups(app, mode);
0172     out << make_subcommands(app, mode);
0173     detail::streamOutAsParagraph(out, make_footer(app), footer_paragraph_width_);  // Format footer as paragraph
0174 
0175     return out.str();
0176 }
0177 
0178 CLI11_INLINE std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const {
0179     std::stringstream out;
0180 
0181     std::vector<const App *> subcommands = app->get_subcommands({});
0182 
0183     // Make a list in definition order of the groups seen
0184     std::vector<std::string> subcmd_groups_seen;
0185     for(const App *com : subcommands) {
0186         if(com->get_name().empty()) {
0187             if(!com->get_group().empty() && com->get_group().front() != '+') {
0188                 out << make_expanded(com, mode);
0189             }
0190             continue;
0191         }
0192         std::string group_key = com->get_group();
0193         if(!group_key.empty() &&
0194            std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) {
0195                return detail::to_lower(a) == detail::to_lower(group_key);
0196            }) == subcmd_groups_seen.end())
0197             subcmd_groups_seen.push_back(group_key);
0198     }
0199 
0200     // For each group, filter out and print subcommands
0201     for(const std::string &group : subcmd_groups_seen) {
0202         out << '\n' << group << ":\n";
0203         std::vector<const App *> subcommands_group = app->get_subcommands(
0204             [&group](const App *sub_app) { return detail::to_lower(sub_app->get_group()) == detail::to_lower(group); });
0205         for(const App *new_com : subcommands_group) {
0206             if(new_com->get_name().empty())
0207                 continue;
0208             if(mode != AppFormatMode::All) {
0209                 out << make_subcommand(new_com);
0210             } else {
0211                 out << new_com->help(new_com->get_name(), AppFormatMode::Sub);
0212                 out << '\n';
0213             }
0214         }
0215     }
0216 
0217     return out.str();
0218 }
0219 
0220 CLI11_INLINE std::string Formatter::make_subcommand(const App *sub) const {
0221     std::stringstream out;
0222     std::string name = "  " + sub->get_display_name(true) + (sub->get_required() ? " " + get_label("REQUIRED") : "");
0223 
0224     out << std::setw(static_cast<int>(column_width_)) << std::left << name;
0225     detail::streamOutAsParagraph(
0226         out, sub->get_description(), right_column_width_, std::string(column_width_, ' '), true);
0227     out << '\n';
0228     return out.str();
0229 }
0230 
0231 CLI11_INLINE std::string Formatter::make_expanded(const App *sub, AppFormatMode mode) const {
0232     std::stringstream out;
0233     out << sub->get_display_name(true) << '\n';
0234 
0235     detail::streamOutAsParagraph(
0236         out, make_description(sub), description_paragraph_width_, "  ");  // Format description as paragraph
0237 
0238     if(sub->get_name().empty() && !sub->get_aliases().empty()) {
0239         detail::format_aliases(out, sub->get_aliases(), column_width_ + 2);
0240     }
0241 
0242     out << make_positionals(sub);
0243     out << make_groups(sub, mode);
0244     out << make_subcommands(sub, mode);
0245     detail::streamOutAsParagraph(out, make_footer(sub), footer_paragraph_width_);  // Format footer as paragraph
0246 
0247     out << '\n';
0248     return out.str();
0249 }
0250 
0251 CLI11_INLINE std::string Formatter::make_option(const Option *opt, bool is_positional) const {
0252     std::stringstream out;
0253     if(is_positional) {
0254         const std::string left = "  " + make_option_name(opt, true) + make_option_opts(opt);
0255         const std::string desc = make_option_desc(opt);
0256         out << std::setw(static_cast<int>(column_width_)) << std::left << left;
0257 
0258         if(!desc.empty()) {
0259             bool skipFirstLinePrefix = true;
0260             if(left.length() >= column_width_) {
0261                 out << '\n';
0262                 skipFirstLinePrefix = false;
0263             }
0264             detail::streamOutAsParagraph(
0265                 out, desc, right_column_width_, std::string(column_width_, ' '), skipFirstLinePrefix);
0266         }
0267     } else {
0268         const std::string namesCombined = make_option_name(opt, false);
0269         const std::string opts = make_option_opts(opt);
0270         const std::string desc = make_option_desc(opt);
0271 
0272         // Split all names at comma and sort them into short names and long names
0273         const auto names = detail::split(namesCombined, ',');
0274         std::vector<std::string> vshortNames;
0275         std::vector<std::string> vlongNames;
0276         std::for_each(names.begin(), names.end(), [&vshortNames, &vlongNames](const std::string &name) {
0277             if(name.find("--", 0) != std::string::npos)
0278                 vlongNames.push_back(name);
0279             else
0280                 vshortNames.push_back(name);
0281         });
0282 
0283         // Assemble short and long names
0284         std::string shortNames = detail::join(vshortNames, ", ");
0285         std::string longNames = detail::join(vlongNames, ", ");
0286 
0287         // Calculate setw sizes
0288         const auto shortNamesColumnWidth = static_cast<int>(column_width_ / 3);  // 33% left for short names
0289         const auto longNamesColumnWidth = static_cast<int>(std::ceil(
0290             static_cast<float>(column_width_) / 3.0f * 2.0f));  // 66% right for long names and options, ceil result
0291         int shortNamesOverSize = 0;
0292 
0293         // Print short names
0294         if(shortNames.length() > 0) {
0295             shortNames = "  " + shortNames;  // Indent
0296             if(longNames.length() == 0 && opts.length() > 0)
0297                 shortNames += opts;  // Add opts if only short names and no long names
0298             if(longNames.length() > 0)
0299                 shortNames += ",";
0300             if(static_cast<int>(shortNames.length()) >= shortNamesColumnWidth) {
0301                 shortNames += " ";
0302                 shortNamesOverSize = static_cast<int>(shortNames.length()) - shortNamesColumnWidth;
0303             }
0304             out << std::setw(shortNamesColumnWidth) << std::left << shortNames;
0305         } else {
0306             out << std::setw(shortNamesColumnWidth) << std::left << "";
0307         }
0308 
0309         // Adjust long name column width in case of short names column reaching into long names column
0310         shortNamesOverSize =
0311             (std::min)(shortNamesOverSize, longNamesColumnWidth);  // Prevent negative result with unsigned integers
0312         const auto adjustedLongNamesColumnWidth = longNamesColumnWidth - shortNamesOverSize;
0313 
0314         // Print long names
0315         if(longNames.length() > 0) {
0316             if(opts.length() > 0)
0317                 longNames += opts;
0318             if(static_cast<int>(longNames.length()) >= adjustedLongNamesColumnWidth)
0319                 longNames += " ";
0320 
0321             out << std::setw(adjustedLongNamesColumnWidth) << std::left << longNames;
0322         } else {
0323             out << std::setw(adjustedLongNamesColumnWidth) << std::left << "";
0324         }
0325 
0326         if(!desc.empty()) {
0327             bool skipFirstLinePrefix = true;
0328             if(out.str().length() > column_width_) {
0329                 out << '\n';
0330                 skipFirstLinePrefix = false;
0331             }
0332             detail::streamOutAsParagraph(
0333                 out, desc, right_column_width_, std::string(column_width_, ' '), skipFirstLinePrefix);
0334         }
0335     }
0336 
0337     out << '\n';
0338     return out.str();
0339 }
0340 
0341 CLI11_INLINE std::string Formatter::make_option_name(const Option *opt, bool is_positional) const {
0342     if(is_positional)
0343         return opt->get_name(true, false);
0344 
0345     return opt->get_name(false, true);
0346 }
0347 
0348 CLI11_INLINE std::string Formatter::make_option_opts(const Option *opt) const {
0349     std::stringstream out;
0350 
0351     if(!opt->get_option_text().empty()) {
0352         out << " " << opt->get_option_text();
0353     } else {
0354         if(opt->get_type_size() != 0) {
0355             if(!opt->get_type_name().empty())
0356                 out << " " << get_label(opt->get_type_name());
0357             if(!opt->get_default_str().empty())
0358                 out << " [" << opt->get_default_str() << "] ";
0359             if(opt->get_expected_max() == detail::expected_max_vector_size)
0360                 out << " ...";
0361             else if(opt->get_expected_min() > 1)
0362                 out << " x " << opt->get_expected();
0363 
0364             if(opt->get_required())
0365                 out << " " << get_label("REQUIRED");
0366         }
0367         if(!opt->get_envname().empty())
0368             out << " (" << get_label("Env") << ":" << opt->get_envname() << ")";
0369         if(!opt->get_needs().empty()) {
0370             out << " " << get_label("Needs") << ":";
0371             for(const Option *op : opt->get_needs())
0372                 out << " " << op->get_name();
0373         }
0374         if(!opt->get_excludes().empty()) {
0375             out << " " << get_label("Excludes") << ":";
0376             for(const Option *op : opt->get_excludes())
0377                 out << " " << op->get_name();
0378         }
0379     }
0380     return out.str();
0381 }
0382 
0383 CLI11_INLINE std::string Formatter::make_option_desc(const Option *opt) const { return opt->get_description(); }
0384 
0385 CLI11_INLINE std::string Formatter::make_option_usage(const Option *opt) const {
0386     // Note that these are positionals usages
0387     std::stringstream out;
0388     out << make_option_name(opt, true);
0389     if(opt->get_expected_max() >= detail::expected_max_vector_size)
0390         out << "...";
0391     else if(opt->get_expected_max() > 1)
0392         out << "(" << opt->get_expected() << "x)";
0393 
0394     return opt->get_required() ? out.str() : "[" + out.str() + "]";
0395 }
0396 // [CLI11:formatter_inl_hpp:end]
0397 }  // namespace CLI