diff --git a/include/cxxopts.hpp b/include/cxxopts.hpp index e89c2e1..f5f2af1 100644 --- a/include/cxxopts.hpp +++ b/include/cxxopts.hpp @@ -1297,6 +1297,8 @@ namespace cxxopts , m_positional_help("positional parameters") , m_show_positional(false) , m_allow_unrecognised(false) + , m_width(76) + , m_tab_expansion(false) , m_options(std::make_shared()) , m_next_positional(m_positional.end()) { @@ -1330,6 +1332,20 @@ namespace cxxopts return *this; } + Options& + set_width(size_t width) + { + m_width = width; + return *this; + } + + Options& + set_tab_expansion(bool expansion=true) + { + m_tab_expansion = expansion; + return *this; + } + ParseResult parse(int& argc, char**& argv); @@ -1414,6 +1430,8 @@ namespace cxxopts std::string m_positional_help; bool m_show_positional; bool m_allow_unrecognised; + size_t m_width; + bool m_tab_expansion; std::shared_ptr m_options; std::vector m_positional; @@ -1450,8 +1468,8 @@ namespace cxxopts namespace { - constexpr int OPTION_LONGEST = 30; - constexpr int OPTION_DESC_GAP = 2; + constexpr size_t OPTION_LONGEST = 30; + constexpr size_t OPTION_DESC_GAP = 2; std::basic_regex option_matcher ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); @@ -1506,7 +1524,8 @@ namespace cxxopts ( const HelpOptionDetails& o, size_t start, - size_t width + size_t width, + bool m_tab_expansion ) { auto desc = o.desc; @@ -1525,53 +1544,85 @@ namespace cxxopts String result; + if (m_tab_expansion) + { + String desc2; + auto size = size_t{ 0 }; + for (auto c = std::begin(desc); c != std::end(desc); ++c) + { + if (*c == '\n') + { + desc2 += *c; + size = 0; + } + else if (*c == '\t') + { + auto skip = 8 - size % 8; + desc2.append(skip, ' '); + size += skip; + } + else + { + desc2 += *c; + ++size; + } + } + desc = desc2; + } + + desc += " "; + auto current = std::begin(desc); auto startLine = current; auto lastSpace = current; - auto size = size_t{}; - while (current != std::end(desc)) { - if (*current == ' ') + size_t addNewLine = 0; + while (*current == '\n') { + *current = ' '; + ++current; + ++addNewLine; + } + if (addNewLine == 0 && current - startLine >= width) + { + if (lastSpace != startLine) + { + current = lastSpace; + } + if (current + 1 != std::end(desc)) + { + ++addNewLine; + } + } + if (addNewLine > 0) + { + stringAppend(result, startLine, current); + startLine = current; lastSpace = current; + + while (addNewLine-- > 0) + { + stringAppend(result, "\n"); + } + + if (current + 1 != std::end(desc)) + { + stringAppend(result, start, ' '); + } } - if (*current == '\n') + if (std::isblank(*current)) { - startLine = current + 1; - lastSpace = startLine; - } - else if (size > width) - { - if (lastSpace == startLine) - { - stringAppend(result, startLine, current + 1); - stringAppend(result, "\n"); - stringAppend(result, start, ' '); - startLine = current + 1; - lastSpace = startLine; - } - else - { - stringAppend(result, startLine, lastSpace); - stringAppend(result, "\n"); - stringAppend(result, start, ' '); - startLine = lastSpace + 1; - } - size = 0; - } - else - { - ++size; + lastSpace = current + 1; } ++current; } //append whatever is left - stringAppend(result, startLine, current); + stringAppend(result, startLine, std::end(desc) - 1); return result; } @@ -2082,11 +2133,14 @@ Options::help_one_group(const std::string& g) const longest = (std::max)(longest, stringLength(s)); format.push_back(std::make_pair(s, String())); } + longest = (std::min)(longest, OPTION_LONGEST); - longest = (std::min)(longest, static_cast(OPTION_LONGEST)); - - //widest allowed description - auto allowed = size_t{76} - longest - OPTION_DESC_GAP; + //widest allowed description -- min 10 chars for helptext/line + size_t allowed = 10; + if (m_width > allowed + longest + OPTION_DESC_GAP) + { + allowed = m_width - longest - OPTION_DESC_GAP; + } auto fiter = format.begin(); for (const auto& o : group->second.options) @@ -2097,7 +2151,7 @@ Options::help_one_group(const std::string& g) const continue; } - auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); + auto d = format_description(o, longest + OPTION_DESC_GAP, allowed, m_tab_expansion); result += fiter->first; if (stringLength(fiter->first) > longest)