diff --git a/include/argparse/argparse.hpp b/include/argparse/argparse.hpp index 633f782..a779e01 100644 --- a/include/argparse/argparse.hpp +++ b/include/argparse/argparse.hpp @@ -673,7 +673,36 @@ public: name_stream << " " << argument.m_metavar; } } - stream << name_stream.str() << "\t" << argument.m_help; + + // align multiline help message + auto stream_width = stream.width(); + auto name_padding = std::string(name_stream.str().size(), ' '); + auto pos = 0; + auto prev = 0; + auto first_line = true; + auto hspace = " "; // minimal space between name and help message + stream << name_stream.str(); + std::string_view help_view(argument.m_help); + while ((pos = argument.m_help.find('\n', prev)) != std::string::npos) { + auto line = help_view.substr(prev, pos - prev + 1); + if (first_line) { + stream << hspace << line; + first_line = false; + } else { + stream.width(stream_width); + stream << name_padding << hspace << line; + } + prev += pos - prev + 1; + } + if (first_line) { + stream << hspace << argument.m_help; + } else { + auto leftover = help_view.substr(prev, argument.m_help.size() - prev); + if (!leftover.empty()) { + stream.width(stream_width); + stream << name_padding << hspace << leftover; + } + } // print nargs spec if (!argument.m_help.empty()) { diff --git a/test/test_help.cpp b/test/test_help.cpp index a81af42..293daef 100644 --- a/test/test_help.cpp +++ b/test/test_help.cpp @@ -73,3 +73,48 @@ TEST_CASE("Users can replace default -h/--help" * test_suite("help")) { program.parse_args({"test", "--help"}); REQUIRE_FALSE(buffer.str().empty()); } + +TEST_CASE("Multiline help message alignment") { + // '#' is used at the beginning of each help message line to simplify testing. + // It is important to ensure that this character doesn't appear elsewhere in the test case. + // Default arguments (e.g., -h/--help, -v/--version) are not included in this test. + argparse::ArgumentParser program("program"); + program.add_argument("INPUT1") + .help( + "#This is the first line of help message.\n" + "#And this is the second line of help message." + ); + program.add_argument("program_input2") + .help("#There is only one line."); + program.add_argument("-p", "--prog_input3") + .help( +R"(#Lorem ipsum dolor sit amet, consectetur adipiscing elit. +#Sed ut perspiciatis unde omnis iste natus error sit voluptatem +#accusantium doloremque laudantium, totam rem aperiam...)" + ); + program.add_argument("--verbose").default_value(false).implicit_value(true); + + std::ostringstream stream; + stream << program; + std::istringstream iss(stream.str()); + + int help_message_start = -1; + std::string line; + while (std::getline(iss, line)) { + // Find the position of '#', which indicates the start of the help message line + auto pos = line.find('#'); + + if (pos == std::string::npos) { + continue; + } + + if (help_message_start == -1) { + help_message_start = pos; + } else { + REQUIRE(pos == help_message_start); + } + } + + // Make sure we have at least one help message + REQUIRE(help_message_start != -1); +}