Improve formatting of help descriptions (#215)

* Improve formatting of help descriptions (#213)

* new function: cxxopts::Option::set_width(size_t width)
  Set the size of a helpline.
* new function: cxxopts::Option::set_tab_expansion()
  Expand the tabs in descriptions.
  The tabsize 8 chars, base is start of description.
  The descriptions are not disturbed by adding additional options.
* Allow newlines \n and tabs \t in descriptions.

Other changes (last commit/new commit):
* 1453/1471: size_t for OPTION_LONGEST and OPTION_DESC_GAP.
  This prevents the static cast in 2086/2140.
* 2088/2142: in case of small width the value of
  "width - longest - OPTION_DEC_GAP" becomes negative.
  Because size_t is unsigned the result is a big number, and
  the width of the column of the descriptions is not shortened.
* new 2143: When the given width is too small, it is set to
  longest + OPTION_DESC_GAP + 10
* new 1570: A long description is broken into multiple lines, and
  the iterator lastSpace remembers the begin of the last word.
  But when the iterator current reaches the end of line, the whole
  string from iterator is printed, which in soome cases is too
  long. Thats why one blank is added to the description to trigger
  the handling of lastSpace.
  Accordingly in 1574/1627 the line is shortened by one char.

* repaired signed/unsigned issue

* changes for unicode
This commit is contained in:
Wolfgang Gahr 2021-02-11 22:17:55 +01:00 committed by GitHub
parent f34d603863
commit 43ce03fdbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1403,6 +1403,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<OptionMap>())
{
}
@ -1435,6 +1437,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, const char* const* argv);
@ -1519,6 +1535,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<OptionMap> m_options;
std::vector<std::string> m_positional{};
@ -1557,8 +1575,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<char> option_matcher
("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
@ -1617,7 +1635,8 @@ namespace cxxopts
(
const HelpOptionDetails& o,
size_t start,
size_t width
size_t allowed,
bool m_tab_expansion
)
{
auto desc = o.desc;
@ -1636,54 +1655,107 @@ 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;
stringAppend(desc2, skip, ' ');
size += skip;
}
else
{
desc2 += *c;
++size;
}
}
desc = desc2;
}
desc += " ";
auto current = std::begin(desc);
auto previous = current;
auto startLine = current;
auto lastSpace = current;
auto size = size_t{};
bool appendNewLine;
bool onlyWhiteSpace = true;
while (current != std::end(desc))
{
if (*current == ' ')
appendNewLine = false;
if (std::isblank(*previous))
{
lastSpace = current;
}
if (*current == '\n')
if (!std::isblank(*current))
{
startLine = current + 1;
lastSpace = startLine;
onlyWhiteSpace = false;
}
else if (size > width)
while (*current == '\n')
{
if (lastSpace == startLine)
previous = current;
++current;
appendNewLine = true;
}
if (!appendNewLine && size >= allowed)
{
if (lastSpace != startLine)
{
stringAppend(result, startLine, current + 1);
stringAppend(result, "\n");
stringAppend(result, start, ' ');
startLine = current + 1;
lastSpace = startLine;
current = lastSpace;
previous = current;
}
else
appendNewLine = true;
}
if (appendNewLine)
{
stringAppend(result, startLine, current);
startLine = current;
lastSpace = current;
if (*previous != '\n')
{
stringAppend(result, startLine, lastSpace);
stringAppend(result, "\n");
stringAppend(result, start, ' ');
startLine = lastSpace + 1;
lastSpace = startLine;
}
stringAppend(result, start, ' ');
if (*previous != '\n')
{
stringAppend(result, lastSpace, current);
}
onlyWhiteSpace = true;
size = 0;
}
else
{
++size;
}
previous = current;
++current;
++size;
}
//append whatever is left
stringAppend(result, startLine, current);
//append whatever is left but ignore whitespace
if (!onlyWhiteSpace)
{
stringAppend(result, startLine, previous);
}
return result;
}
@ -2172,11 +2244,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<size_t>(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)
@ -2187,7 +2262,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)