diff --git a/CHANGELOG.md b/CHANGELOG.md index 64f2437..ba405ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,13 @@ -## Version 1.6.2: More special case fixes +## Version 1.6.2: Help-all -This version adds fixes for several warnings, including an experimental optional error on Clang 7. +This version fixes some formatting bugs with help-all. It also adds fixes for several warnings, including an experimental optional error on Clang 7. +* Fixed help-all formatting [#163] + * Printing help-all on nested command now fixed (App) + * Missing space after help-all restored (Default formatter) + * More detail printed on help all (Default formatter) + * Help-all subcommands get indented with inner blank lines removed (Default formatter) + * `detail::find_and_replace` added to utilities * Fixed CMake install as subproject with `CLI11_INSTALL` flag. [#156] * Fixed warning about local variable hiding class member with MSVC [#157] * Fixed compile error with default settings on Clang 7 and libc++ [#158] @@ -9,6 +15,7 @@ This version adds fixes for several warnings, including an experimental optional [#156]: https://github.com/CLIUtils/CLI11/issues/156 [#157]: https://github.com/CLIUtils/CLI11/issues/157 [#158]: https://github.com/CLIUtils/CLI11/issues/158 +[#163]: https://github.com/CLIUtils/CLI11/pull/163 ## Version 1.6.1: Platform fixes diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f967ec5..ef17d2c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -126,3 +126,5 @@ set_property(TEST subcom_in_files PROPERTY PASS_REGULAR_EXPRESSION "Using foo!") add_cli_exe(formatter formatter.cpp) + +add_cli_exe(nested nested.cpp) diff --git a/examples/nested.cpp b/examples/nested.cpp new file mode 100644 index 0000000..1714b8d --- /dev/null +++ b/examples/nested.cpp @@ -0,0 +1,22 @@ +#include +#include + +int main(int argc, char **argv) { + + CLI::App app("Vision Application"); + app.set_help_all_flag("--help-all", "Expand all help"); + app.add_flag("--version", "Get version"); + + CLI::App *cameraApp = app.add_subcommand("camera", "Configure the app camera"); + cameraApp->require_subcommand(0, 1); // 0 (default) or 1 camera + + std::string mvcamera_config_file = "mvcamera_config.json"; + CLI::App *mvcameraApp = cameraApp->add_subcommand("mvcamera", "MatrixVision Camera Configuration"); + mvcameraApp->add_option("-c,--config", mvcamera_config_file, "Config filename", true)->check(CLI::ExistingFile); + + std::string mock_camera_path; + CLI::App *mockcameraApp = cameraApp->add_subcommand("mock", "Mock Camera Configuration"); + mockcameraApp->add_option("-p,--path", mock_camera_path, "Path")->required()->check(CLI::ExistingPath); + + CLI11_PARSE(app, argc, argv); +} diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 80c31ba..eebab3b 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -1043,7 +1043,7 @@ class App { // Delegate to subcommand if needed auto selected_subcommands = get_subcommands(); if(!selected_subcommands.empty()) - return selected_subcommands.at(0)->help(prev); + return selected_subcommands.at(0)->help(prev, mode); else return formatter_->make_help(this, prev, mode); } diff --git a/include/CLI/Formatter.hpp b/include/CLI/Formatter.hpp index 82e641a..7f1885e 100644 --- a/include/CLI/Formatter.hpp +++ b/include/CLI/Formatter.hpp @@ -158,8 +158,7 @@ inline std::string Formatter::make_subcommands(const App *app, AppFormatMode mod out << make_subcommand(new_com); } else { out << new_com->help(new_com->get_name(), AppFormatMode::Sub); - if(new_com != subcommands_group.back()) - out << "\n"; + out << "\n"; } } } @@ -175,12 +174,19 @@ inline std::string Formatter::make_subcommand(const App *sub) const { inline std::string Formatter::make_expanded(const App *sub) const { std::stringstream out; - if(sub->get_description().empty()) - out << sub->get_name(); - else - out << sub->get_name() << " -> " << sub->get_description(); + out << sub->get_name() << "\n"; + + out << make_description(sub); + out << make_positionals(sub); out << make_groups(sub, AppFormatMode::Sub); - return out.str(); + out << make_subcommands(sub, AppFormatMode::Sub); + + // Drop blank spaces + std::string tmp = detail::find_and_replace(out.str(), "\n\n", "\n"); + tmp = tmp.substr(0, tmp.size() - 1); // Remove the final '\n' + + // Indent all but the first line (the name) + return detail::find_and_replace(tmp, "\n", "\n ") + "\n"; } inline std::string Formatter::make_option_name(const Option *opt, bool is_positional) const { diff --git a/include/CLI/StringTools.hpp b/include/CLI/StringTools.hpp index 24e01bc..1b49783 100644 --- a/include/CLI/StringTools.hpp +++ b/include/CLI/StringTools.hpp @@ -204,5 +204,18 @@ inline std::string fix_newlines(std::string leader, std::string input) { return input; } +/// Find and replace a subtring with another substring +inline std::string find_and_replace(std::string str, std::string from, std::string to) { + + size_t start_pos = 0; + + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } + + return str; +} + } // namespace detail } // namespace CLI diff --git a/tests/HelpTest.cpp b/tests/HelpTest.cpp index e9ecdd6..8825b08 100644 --- a/tests/HelpTest.cpp +++ b/tests/HelpTest.cpp @@ -511,10 +511,11 @@ TEST_F(CapturedHelp, CallForAllHelpOutput) { " --help-all Help all\n" "\n" "Subcommands:\n" - "one -> One description\n" + "one\n" + " One description\n\n" "two\n" - "Options:\n" - " --three \n"); + " Options:\n" + " --three \n\n"); } TEST_F(CapturedHelp, NewFormattedHelp) { app.formatter_fn([](const CLI::App *, std::string, CLI::AppFormatMode) { return "New Help"; });