Commit c24bf7541398ffeb68e7343154b93f9a630e2147
Committed by
Henry Schreiner
1 parent
003e8257
[precompile] Split Formatter.hpp
Showing
4 changed files
with
304 additions
and
271 deletions
CLI11.hpp.in
include/CLI/Formatter.hpp
| @@ -17,276 +17,9 @@ | @@ -17,276 +17,9 @@ | ||
| 17 | 17 | ||
| 18 | namespace CLI { | 18 | namespace CLI { |
| 19 | // [CLI11:formatter_hpp:verbatim] | 19 | // [CLI11:formatter_hpp:verbatim] |
| 20 | - | ||
| 21 | -inline std::string | ||
| 22 | -Formatter::make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const { | ||
| 23 | - std::stringstream out; | ||
| 24 | - | ||
| 25 | - out << "\n" << group << ":\n"; | ||
| 26 | - for(const Option *opt : opts) { | ||
| 27 | - out << make_option(opt, is_positional); | ||
| 28 | - } | ||
| 29 | - | ||
| 30 | - return out.str(); | ||
| 31 | -} | ||
| 32 | - | ||
| 33 | -inline std::string Formatter::make_positionals(const App *app) const { | ||
| 34 | - std::vector<const Option *> opts = | ||
| 35 | - app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); }); | ||
| 36 | - | ||
| 37 | - if(opts.empty()) | ||
| 38 | - return {}; | ||
| 39 | - | ||
| 40 | - return make_group(get_label("Positionals"), true, opts); | ||
| 41 | -} | ||
| 42 | - | ||
| 43 | -inline std::string Formatter::make_groups(const App *app, AppFormatMode mode) const { | ||
| 44 | - std::stringstream out; | ||
| 45 | - std::vector<std::string> groups = app->get_groups(); | ||
| 46 | - | ||
| 47 | - // Options | ||
| 48 | - for(const std::string &group : groups) { | ||
| 49 | - std::vector<const Option *> opts = app->get_options([app, mode, &group](const Option *opt) { | ||
| 50 | - return opt->get_group() == group // Must be in the right group | ||
| 51 | - && opt->nonpositional() // Must not be a positional | ||
| 52 | - && (mode != AppFormatMode::Sub // If mode is Sub, then | ||
| 53 | - || (app->get_help_ptr() != opt // Ignore help pointer | ||
| 54 | - && app->get_help_all_ptr() != opt)); // Ignore help all pointer | ||
| 55 | - }); | ||
| 56 | - if(!group.empty() && !opts.empty()) { | ||
| 57 | - out << make_group(group, false, opts); | ||
| 58 | - | ||
| 59 | - if(group != groups.back()) | ||
| 60 | - out << "\n"; | ||
| 61 | - } | ||
| 62 | - } | ||
| 63 | - | ||
| 64 | - return out.str(); | ||
| 65 | -} | ||
| 66 | - | ||
| 67 | -inline std::string Formatter::make_description(const App *app) const { | ||
| 68 | - std::string desc = app->get_description(); | ||
| 69 | - auto min_options = app->get_require_option_min(); | ||
| 70 | - auto max_options = app->get_require_option_max(); | ||
| 71 | - if(app->get_required()) { | ||
| 72 | - desc += " REQUIRED "; | ||
| 73 | - } | ||
| 74 | - if((max_options == min_options) && (min_options > 0)) { | ||
| 75 | - if(min_options == 1) { | ||
| 76 | - desc += " \n[Exactly 1 of the following options is required]"; | ||
| 77 | - } else { | ||
| 78 | - desc += " \n[Exactly " + std::to_string(min_options) + "options from the following list are required]"; | ||
| 79 | - } | ||
| 80 | - } else if(max_options > 0) { | ||
| 81 | - if(min_options > 0) { | ||
| 82 | - desc += " \n[Between " + std::to_string(min_options) + " and " + std::to_string(max_options) + | ||
| 83 | - " of the follow options are required]"; | ||
| 84 | - } else { | ||
| 85 | - desc += " \n[At most " + std::to_string(max_options) + " of the following options are allowed]"; | ||
| 86 | - } | ||
| 87 | - } else if(min_options > 0) { | ||
| 88 | - desc += " \n[At least " + std::to_string(min_options) + " of the following options are required]"; | ||
| 89 | - } | ||
| 90 | - return (!desc.empty()) ? desc + "\n" : std::string{}; | ||
| 91 | -} | ||
| 92 | - | ||
| 93 | -inline std::string Formatter::make_usage(const App *app, std::string name) const { | ||
| 94 | - std::stringstream out; | ||
| 95 | - | ||
| 96 | - out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name; | ||
| 97 | - | ||
| 98 | - std::vector<std::string> groups = app->get_groups(); | ||
| 99 | - | ||
| 100 | - // Print an Options badge if any options exist | ||
| 101 | - std::vector<const Option *> non_pos_options = | ||
| 102 | - app->get_options([](const Option *opt) { return opt->nonpositional(); }); | ||
| 103 | - if(!non_pos_options.empty()) | ||
| 104 | - out << " [" << get_label("OPTIONS") << "]"; | ||
| 105 | - | ||
| 106 | - // Positionals need to be listed here | ||
| 107 | - std::vector<const Option *> positionals = app->get_options([](const Option *opt) { return opt->get_positional(); }); | ||
| 108 | - | ||
| 109 | - // Print out positionals if any are left | ||
| 110 | - if(!positionals.empty()) { | ||
| 111 | - // Convert to help names | ||
| 112 | - std::vector<std::string> positional_names(positionals.size()); | ||
| 113 | - std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [this](const Option *opt) { | ||
| 114 | - return make_option_usage(opt); | ||
| 115 | - }); | ||
| 116 | - | ||
| 117 | - out << " " << detail::join(positional_names, " "); | ||
| 118 | - } | ||
| 119 | - | ||
| 120 | - // Add a marker if subcommands are expected or optional | ||
| 121 | - if(!app->get_subcommands( | ||
| 122 | - [](const CLI::App *subc) { return ((!subc->get_disabled()) && (!subc->get_name().empty())); }) | ||
| 123 | - .empty()) { | ||
| 124 | - out << " " << (app->get_require_subcommand_min() == 0 ? "[" : "") | ||
| 125 | - << get_label(app->get_require_subcommand_max() < 2 || app->get_require_subcommand_min() > 1 ? "SUBCOMMAND" | ||
| 126 | - : "SUBCOMMANDS") | ||
| 127 | - << (app->get_require_subcommand_min() == 0 ? "]" : ""); | ||
| 128 | - } | ||
| 129 | - | ||
| 130 | - out << std::endl; | ||
| 131 | - | ||
| 132 | - return out.str(); | ||
| 133 | -} | ||
| 134 | - | ||
| 135 | -inline std::string Formatter::make_footer(const App *app) const { | ||
| 136 | - std::string footer = app->get_footer(); | ||
| 137 | - if(footer.empty()) { | ||
| 138 | - return std::string{}; | ||
| 139 | - } | ||
| 140 | - return footer + "\n"; | ||
| 141 | -} | ||
| 142 | - | ||
| 143 | -inline std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const { | ||
| 144 | - | ||
| 145 | - // This immediately forwards to the make_expanded method. This is done this way so that subcommands can | ||
| 146 | - // have overridden formatters | ||
| 147 | - if(mode == AppFormatMode::Sub) | ||
| 148 | - return make_expanded(app); | ||
| 149 | - | ||
| 150 | - std::stringstream out; | ||
| 151 | - if((app->get_name().empty()) && (app->get_parent() != nullptr)) { | ||
| 152 | - if(app->get_group() != "Subcommands") { | ||
| 153 | - out << app->get_group() << ':'; | ||
| 154 | - } | ||
| 155 | - } | ||
| 156 | - | ||
| 157 | - out << make_description(app); | ||
| 158 | - out << make_usage(app, name); | ||
| 159 | - out << make_positionals(app); | ||
| 160 | - out << make_groups(app, mode); | ||
| 161 | - out << make_subcommands(app, mode); | ||
| 162 | - out << '\n' << make_footer(app); | ||
| 163 | - | ||
| 164 | - return out.str(); | ||
| 165 | -} | ||
| 166 | - | ||
| 167 | -inline std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const { | ||
| 168 | - std::stringstream out; | ||
| 169 | - | ||
| 170 | - std::vector<const App *> subcommands = app->get_subcommands({}); | ||
| 171 | - | ||
| 172 | - // Make a list in definition order of the groups seen | ||
| 173 | - std::vector<std::string> subcmd_groups_seen; | ||
| 174 | - for(const App *com : subcommands) { | ||
| 175 | - if(com->get_name().empty()) { | ||
| 176 | - if(!com->get_group().empty()) { | ||
| 177 | - out << make_expanded(com); | ||
| 178 | - } | ||
| 179 | - continue; | ||
| 180 | - } | ||
| 181 | - std::string group_key = com->get_group(); | ||
| 182 | - if(!group_key.empty() && | ||
| 183 | - std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) { | ||
| 184 | - return detail::to_lower(a) == detail::to_lower(group_key); | ||
| 185 | - }) == subcmd_groups_seen.end()) | ||
| 186 | - subcmd_groups_seen.push_back(group_key); | ||
| 187 | - } | ||
| 188 | - | ||
| 189 | - // For each group, filter out and print subcommands | ||
| 190 | - for(const std::string &group : subcmd_groups_seen) { | ||
| 191 | - out << "\n" << group << ":\n"; | ||
| 192 | - std::vector<const App *> subcommands_group = app->get_subcommands( | ||
| 193 | - [&group](const App *sub_app) { return detail::to_lower(sub_app->get_group()) == detail::to_lower(group); }); | ||
| 194 | - for(const App *new_com : subcommands_group) { | ||
| 195 | - if(new_com->get_name().empty()) | ||
| 196 | - continue; | ||
| 197 | - if(mode != AppFormatMode::All) { | ||
| 198 | - out << make_subcommand(new_com); | ||
| 199 | - } else { | ||
| 200 | - out << new_com->help(new_com->get_name(), AppFormatMode::Sub); | ||
| 201 | - out << "\n"; | ||
| 202 | - } | ||
| 203 | - } | ||
| 204 | - } | ||
| 205 | - | ||
| 206 | - return out.str(); | ||
| 207 | -} | ||
| 208 | - | ||
| 209 | -inline std::string Formatter::make_subcommand(const App *sub) const { | ||
| 210 | - std::stringstream out; | ||
| 211 | - detail::format_help(out, sub->get_display_name(true), sub->get_description(), column_width_); | ||
| 212 | - return out.str(); | ||
| 213 | -} | ||
| 214 | - | ||
| 215 | -inline std::string Formatter::make_expanded(const App *sub) const { | ||
| 216 | - std::stringstream out; | ||
| 217 | - out << sub->get_display_name(true) << "\n"; | ||
| 218 | - | ||
| 219 | - out << make_description(sub); | ||
| 220 | - if(sub->get_name().empty() && !sub->get_aliases().empty()) { | ||
| 221 | - detail::format_aliases(out, sub->get_aliases(), column_width_ + 2); | ||
| 222 | - } | ||
| 223 | - out << make_positionals(sub); | ||
| 224 | - out << make_groups(sub, AppFormatMode::Sub); | ||
| 225 | - out << make_subcommands(sub, AppFormatMode::Sub); | ||
| 226 | - | ||
| 227 | - // Drop blank spaces | ||
| 228 | - std::string tmp = detail::find_and_replace(out.str(), "\n\n", "\n"); | ||
| 229 | - tmp = tmp.substr(0, tmp.size() - 1); // Remove the final '\n' | ||
| 230 | - | ||
| 231 | - // Indent all but the first line (the name) | ||
| 232 | - return detail::find_and_replace(tmp, "\n", "\n ") + "\n"; | ||
| 233 | -} | ||
| 234 | - | ||
| 235 | -inline std::string Formatter::make_option_name(const Option *opt, bool is_positional) const { | ||
| 236 | - if(is_positional) | ||
| 237 | - return opt->get_name(true, false); | ||
| 238 | - | ||
| 239 | - return opt->get_name(false, true); | ||
| 240 | -} | ||
| 241 | - | ||
| 242 | -inline std::string Formatter::make_option_opts(const Option *opt) const { | ||
| 243 | - std::stringstream out; | ||
| 244 | - | ||
| 245 | - if(!opt->get_option_text().empty()) { | ||
| 246 | - out << " " << opt->get_option_text(); | ||
| 247 | - } else { | ||
| 248 | - if(opt->get_type_size() != 0) { | ||
| 249 | - if(!opt->get_type_name().empty()) | ||
| 250 | - out << " " << get_label(opt->get_type_name()); | ||
| 251 | - if(!opt->get_default_str().empty()) | ||
| 252 | - out << " [" << opt->get_default_str() << "] "; | ||
| 253 | - if(opt->get_expected_max() == detail::expected_max_vector_size) | ||
| 254 | - out << " ..."; | ||
| 255 | - else if(opt->get_expected_min() > 1) | ||
| 256 | - out << " x " << opt->get_expected(); | ||
| 257 | - | ||
| 258 | - if(opt->get_required()) | ||
| 259 | - out << " " << get_label("REQUIRED"); | ||
| 260 | - } | ||
| 261 | - if(!opt->get_envname().empty()) | ||
| 262 | - out << " (" << get_label("Env") << ":" << opt->get_envname() << ")"; | ||
| 263 | - if(!opt->get_needs().empty()) { | ||
| 264 | - out << " " << get_label("Needs") << ":"; | ||
| 265 | - for(const Option *op : opt->get_needs()) | ||
| 266 | - out << " " << op->get_name(); | ||
| 267 | - } | ||
| 268 | - if(!opt->get_excludes().empty()) { | ||
| 269 | - out << " " << get_label("Excludes") << ":"; | ||
| 270 | - for(const Option *op : opt->get_excludes()) | ||
| 271 | - out << " " << op->get_name(); | ||
| 272 | - } | ||
| 273 | - } | ||
| 274 | - return out.str(); | ||
| 275 | -} | ||
| 276 | - | ||
| 277 | -inline std::string Formatter::make_option_desc(const Option *opt) const { return opt->get_description(); } | ||
| 278 | - | ||
| 279 | -inline std::string Formatter::make_option_usage(const Option *opt) const { | ||
| 280 | - // Note that these are positionals usages | ||
| 281 | - std::stringstream out; | ||
| 282 | - out << make_option_name(opt, true); | ||
| 283 | - if(opt->get_expected_max() >= detail::expected_max_vector_size) | ||
| 284 | - out << "..."; | ||
| 285 | - else if(opt->get_expected_max() > 1) | ||
| 286 | - out << "(" << opt->get_expected() << "x)"; | ||
| 287 | - | ||
| 288 | - return opt->get_required() ? out.str() : "[" + out.str() + "]"; | ||
| 289 | -} | ||
| 290 | - | ||
| 291 | // [CLI11:formatter_hpp:end] | 20 | // [CLI11:formatter_hpp:end] |
| 292 | } // namespace CLI | 21 | } // namespace CLI |
| 22 | + | ||
| 23 | +#ifndef CLI11_COMPILE | ||
| 24 | +#include "impl/Formatter_inl.hpp" | ||
| 25 | +#endif |
include/CLI/impl/Formatter_inl.hpp
0 → 100644
| 1 | +// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner | ||
| 2 | +// under NSF AWARD 1414736 and by the respective contributors. | ||
| 3 | +// All rights reserved. | ||
| 4 | +// | ||
| 5 | +// SPDX-License-Identifier: BSD-3-Clause | ||
| 6 | + | ||
| 7 | +#pragma once | ||
| 8 | + | ||
| 9 | +// This include is only needed for IDEs to discover symbols | ||
| 10 | +#include <CLI/Formatter.hpp> | ||
| 11 | + | ||
| 12 | +// [CLI11:public_includes:set] | ||
| 13 | +#include <algorithm> | ||
| 14 | +#include <string> | ||
| 15 | +#include <utility> | ||
| 16 | +#include <vector> | ||
| 17 | +// [CLI11:public_includes:end] | ||
| 18 | + | ||
| 19 | +namespace CLI { | ||
| 20 | +// [CLI11:formatter_inl_hpp:verbatim] | ||
| 21 | +CLI11_INLINE std::string | ||
| 22 | +Formatter::make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const { | ||
| 23 | + std::stringstream out; | ||
| 24 | + | ||
| 25 | + out << "\n" << group << ":\n"; | ||
| 26 | + for(const Option *opt : opts) { | ||
| 27 | + out << make_option(opt, is_positional); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + return out.str(); | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +CLI11_INLINE std::string Formatter::make_positionals(const App *app) const { | ||
| 34 | + std::vector<const Option *> opts = | ||
| 35 | + app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); }); | ||
| 36 | + | ||
| 37 | + if(opts.empty()) | ||
| 38 | + return {}; | ||
| 39 | + | ||
| 40 | + return make_group(get_label("Positionals"), true, opts); | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +CLI11_INLINE std::string Formatter::make_groups(const App *app, AppFormatMode mode) const { | ||
| 44 | + std::stringstream out; | ||
| 45 | + std::vector<std::string> groups = app->get_groups(); | ||
| 46 | + | ||
| 47 | + // Options | ||
| 48 | + for(const std::string &group : groups) { | ||
| 49 | + std::vector<const Option *> opts = app->get_options([app, mode, &group](const Option *opt) { | ||
| 50 | + return opt->get_group() == group // Must be in the right group | ||
| 51 | + && opt->nonpositional() // Must not be a positional | ||
| 52 | + && (mode != AppFormatMode::Sub // If mode is Sub, then | ||
| 53 | + || (app->get_help_ptr() != opt // Ignore help pointer | ||
| 54 | + && app->get_help_all_ptr() != opt)); // Ignore help all pointer | ||
| 55 | + }); | ||
| 56 | + if(!group.empty() && !opts.empty()) { | ||
| 57 | + out << make_group(group, false, opts); | ||
| 58 | + | ||
| 59 | + if(group != groups.back()) | ||
| 60 | + out << "\n"; | ||
| 61 | + } | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + return out.str(); | ||
| 65 | +} | ||
| 66 | + | ||
| 67 | +CLI11_INLINE std::string Formatter::make_description(const App *app) const { | ||
| 68 | + std::string desc = app->get_description(); | ||
| 69 | + auto min_options = app->get_require_option_min(); | ||
| 70 | + auto max_options = app->get_require_option_max(); | ||
| 71 | + if(app->get_required()) { | ||
| 72 | + desc += " REQUIRED "; | ||
| 73 | + } | ||
| 74 | + if((max_options == min_options) && (min_options > 0)) { | ||
| 75 | + if(min_options == 1) { | ||
| 76 | + desc += " \n[Exactly 1 of the following options is required]"; | ||
| 77 | + } else { | ||
| 78 | + desc += " \n[Exactly " + std::to_string(min_options) + "options from the following list are required]"; | ||
| 79 | + } | ||
| 80 | + } else if(max_options > 0) { | ||
| 81 | + if(min_options > 0) { | ||
| 82 | + desc += " \n[Between " + std::to_string(min_options) + " and " + std::to_string(max_options) + | ||
| 83 | + " of the follow options are required]"; | ||
| 84 | + } else { | ||
| 85 | + desc += " \n[At most " + std::to_string(max_options) + " of the following options are allowed]"; | ||
| 86 | + } | ||
| 87 | + } else if(min_options > 0) { | ||
| 88 | + desc += " \n[At least " + std::to_string(min_options) + " of the following options are required]"; | ||
| 89 | + } | ||
| 90 | + return (!desc.empty()) ? desc + "\n" : std::string{}; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +CLI11_INLINE std::string Formatter::make_usage(const App *app, std::string name) const { | ||
| 94 | + std::stringstream out; | ||
| 95 | + | ||
| 96 | + out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name; | ||
| 97 | + | ||
| 98 | + std::vector<std::string> groups = app->get_groups(); | ||
| 99 | + | ||
| 100 | + // Print an Options badge if any options exist | ||
| 101 | + std::vector<const Option *> non_pos_options = | ||
| 102 | + app->get_options([](const Option *opt) { return opt->nonpositional(); }); | ||
| 103 | + if(!non_pos_options.empty()) | ||
| 104 | + out << " [" << get_label("OPTIONS") << "]"; | ||
| 105 | + | ||
| 106 | + // Positionals need to be listed here | ||
| 107 | + std::vector<const Option *> positionals = app->get_options([](const Option *opt) { return opt->get_positional(); }); | ||
| 108 | + | ||
| 109 | + // Print out positionals if any are left | ||
| 110 | + if(!positionals.empty()) { | ||
| 111 | + // Convert to help names | ||
| 112 | + std::vector<std::string> positional_names(positionals.size()); | ||
| 113 | + std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [this](const Option *opt) { | ||
| 114 | + return make_option_usage(opt); | ||
| 115 | + }); | ||
| 116 | + | ||
| 117 | + out << " " << detail::join(positional_names, " "); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + // Add a marker if subcommands are expected or optional | ||
| 121 | + if(!app->get_subcommands( | ||
| 122 | + [](const CLI::App *subc) { return ((!subc->get_disabled()) && (!subc->get_name().empty())); }) | ||
| 123 | + .empty()) { | ||
| 124 | + out << " " << (app->get_require_subcommand_min() == 0 ? "[" : "") | ||
| 125 | + << get_label(app->get_require_subcommand_max() < 2 || app->get_require_subcommand_min() > 1 ? "SUBCOMMAND" | ||
| 126 | + : "SUBCOMMANDS") | ||
| 127 | + << (app->get_require_subcommand_min() == 0 ? "]" : ""); | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + out << std::endl; | ||
| 131 | + | ||
| 132 | + return out.str(); | ||
| 133 | +} | ||
| 134 | + | ||
| 135 | +CLI11_INLINE std::string Formatter::make_footer(const App *app) const { | ||
| 136 | + std::string footer = app->get_footer(); | ||
| 137 | + if(footer.empty()) { | ||
| 138 | + return std::string{}; | ||
| 139 | + } | ||
| 140 | + return footer + "\n"; | ||
| 141 | +} | ||
| 142 | + | ||
| 143 | +CLI11_INLINE std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const { | ||
| 144 | + | ||
| 145 | + // This immediately forwards to the make_expanded method. This is done this way so that subcommands can | ||
| 146 | + // have overridden formatters | ||
| 147 | + if(mode == AppFormatMode::Sub) | ||
| 148 | + return make_expanded(app); | ||
| 149 | + | ||
| 150 | + std::stringstream out; | ||
| 151 | + if((app->get_name().empty()) && (app->get_parent() != nullptr)) { | ||
| 152 | + if(app->get_group() != "Subcommands") { | ||
| 153 | + out << app->get_group() << ':'; | ||
| 154 | + } | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + out << make_description(app); | ||
| 158 | + out << make_usage(app, name); | ||
| 159 | + out << make_positionals(app); | ||
| 160 | + out << make_groups(app, mode); | ||
| 161 | + out << make_subcommands(app, mode); | ||
| 162 | + out << '\n' << make_footer(app); | ||
| 163 | + | ||
| 164 | + return out.str(); | ||
| 165 | +} | ||
| 166 | + | ||
| 167 | +CLI11_INLINE std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const { | ||
| 168 | + std::stringstream out; | ||
| 169 | + | ||
| 170 | + std::vector<const App *> subcommands = app->get_subcommands({}); | ||
| 171 | + | ||
| 172 | + // Make a list in definition order of the groups seen | ||
| 173 | + std::vector<std::string> subcmd_groups_seen; | ||
| 174 | + for(const App *com : subcommands) { | ||
| 175 | + if(com->get_name().empty()) { | ||
| 176 | + if(!com->get_group().empty()) { | ||
| 177 | + out << make_expanded(com); | ||
| 178 | + } | ||
| 179 | + continue; | ||
| 180 | + } | ||
| 181 | + std::string group_key = com->get_group(); | ||
| 182 | + if(!group_key.empty() && | ||
| 183 | + std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) { | ||
| 184 | + return detail::to_lower(a) == detail::to_lower(group_key); | ||
| 185 | + }) == subcmd_groups_seen.end()) | ||
| 186 | + subcmd_groups_seen.push_back(group_key); | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + // For each group, filter out and print subcommands | ||
| 190 | + for(const std::string &group : subcmd_groups_seen) { | ||
| 191 | + out << "\n" << group << ":\n"; | ||
| 192 | + std::vector<const App *> subcommands_group = app->get_subcommands( | ||
| 193 | + [&group](const App *sub_app) { return detail::to_lower(sub_app->get_group()) == detail::to_lower(group); }); | ||
| 194 | + for(const App *new_com : subcommands_group) { | ||
| 195 | + if(new_com->get_name().empty()) | ||
| 196 | + continue; | ||
| 197 | + if(mode != AppFormatMode::All) { | ||
| 198 | + out << make_subcommand(new_com); | ||
| 199 | + } else { | ||
| 200 | + out << new_com->help(new_com->get_name(), AppFormatMode::Sub); | ||
| 201 | + out << "\n"; | ||
| 202 | + } | ||
| 203 | + } | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + return out.str(); | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +CLI11_INLINE std::string Formatter::make_subcommand(const App *sub) const { | ||
| 210 | + std::stringstream out; | ||
| 211 | + detail::format_help(out, sub->get_display_name(true), sub->get_description(), column_width_); | ||
| 212 | + return out.str(); | ||
| 213 | +} | ||
| 214 | + | ||
| 215 | +CLI11_INLINE std::string Formatter::make_expanded(const App *sub) const { | ||
| 216 | + std::stringstream out; | ||
| 217 | + out << sub->get_display_name(true) << "\n"; | ||
| 218 | + | ||
| 219 | + out << make_description(sub); | ||
| 220 | + if(sub->get_name().empty() && !sub->get_aliases().empty()) { | ||
| 221 | + detail::format_aliases(out, sub->get_aliases(), column_width_ + 2); | ||
| 222 | + } | ||
| 223 | + out << make_positionals(sub); | ||
| 224 | + out << make_groups(sub, AppFormatMode::Sub); | ||
| 225 | + out << make_subcommands(sub, AppFormatMode::Sub); | ||
| 226 | + | ||
| 227 | + // Drop blank spaces | ||
| 228 | + std::string tmp = detail::find_and_replace(out.str(), "\n\n", "\n"); | ||
| 229 | + tmp = tmp.substr(0, tmp.size() - 1); // Remove the final '\n' | ||
| 230 | + | ||
| 231 | + // Indent all but the first line (the name) | ||
| 232 | + return detail::find_and_replace(tmp, "\n", "\n ") + "\n"; | ||
| 233 | +} | ||
| 234 | + | ||
| 235 | +CLI11_INLINE std::string Formatter::make_option_name(const Option *opt, bool is_positional) const { | ||
| 236 | + if(is_positional) | ||
| 237 | + return opt->get_name(true, false); | ||
| 238 | + | ||
| 239 | + return opt->get_name(false, true); | ||
| 240 | +} | ||
| 241 | + | ||
| 242 | +CLI11_INLINE std::string Formatter::make_option_opts(const Option *opt) const { | ||
| 243 | + std::stringstream out; | ||
| 244 | + | ||
| 245 | + if(!opt->get_option_text().empty()) { | ||
| 246 | + out << " " << opt->get_option_text(); | ||
| 247 | + } else { | ||
| 248 | + if(opt->get_type_size() != 0) { | ||
| 249 | + if(!opt->get_type_name().empty()) | ||
| 250 | + out << " " << get_label(opt->get_type_name()); | ||
| 251 | + if(!opt->get_default_str().empty()) | ||
| 252 | + out << " [" << opt->get_default_str() << "] "; | ||
| 253 | + if(opt->get_expected_max() == detail::expected_max_vector_size) | ||
| 254 | + out << " ..."; | ||
| 255 | + else if(opt->get_expected_min() > 1) | ||
| 256 | + out << " x " << opt->get_expected(); | ||
| 257 | + | ||
| 258 | + if(opt->get_required()) | ||
| 259 | + out << " " << get_label("REQUIRED"); | ||
| 260 | + } | ||
| 261 | + if(!opt->get_envname().empty()) | ||
| 262 | + out << " (" << get_label("Env") << ":" << opt->get_envname() << ")"; | ||
| 263 | + if(!opt->get_needs().empty()) { | ||
| 264 | + out << " " << get_label("Needs") << ":"; | ||
| 265 | + for(const Option *op : opt->get_needs()) | ||
| 266 | + out << " " << op->get_name(); | ||
| 267 | + } | ||
| 268 | + if(!opt->get_excludes().empty()) { | ||
| 269 | + out << " " << get_label("Excludes") << ":"; | ||
| 270 | + for(const Option *op : opt->get_excludes()) | ||
| 271 | + out << " " << op->get_name(); | ||
| 272 | + } | ||
| 273 | + } | ||
| 274 | + return out.str(); | ||
| 275 | +} | ||
| 276 | + | ||
| 277 | +CLI11_INLINE std::string Formatter::make_option_desc(const Option *opt) const { return opt->get_description(); } | ||
| 278 | + | ||
| 279 | +CLI11_INLINE std::string Formatter::make_option_usage(const Option *opt) const { | ||
| 280 | + // Note that these are positionals usages | ||
| 281 | + std::stringstream out; | ||
| 282 | + out << make_option_name(opt, true); | ||
| 283 | + if(opt->get_expected_max() >= detail::expected_max_vector_size) | ||
| 284 | + out << "..."; | ||
| 285 | + else if(opt->get_expected_max() > 1) | ||
| 286 | + out << "(" << opt->get_expected() << "x)"; | ||
| 287 | + | ||
| 288 | + return opt->get_required() ? out.str() : "[" + out.str() + "]"; | ||
| 289 | +} | ||
| 290 | +// [CLI11:formatter_inl_hpp:end] | ||
| 291 | +} // namespace CLI |
src/Formatter.cpp
0 → 100644