Commit 43ce03fdbd850385b0461c8873771dc47be5c9b3

Authored by Wolfgang Gahr
Committed by GitHub
1 parent f34d6038

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
Showing 1 changed file with 105 additions and 30 deletions
include/cxxopts.hpp
@@ -1403,6 +1403,8 @@ namespace cxxopts @@ -1403,6 +1403,8 @@ namespace cxxopts
1403 , m_positional_help("positional parameters") 1403 , m_positional_help("positional parameters")
1404 , m_show_positional(false) 1404 , m_show_positional(false)
1405 , m_allow_unrecognised(false) 1405 , m_allow_unrecognised(false)
  1406 + , m_width(76)
  1407 + , m_tab_expansion(false)
1406 , m_options(std::make_shared<OptionMap>()) 1408 , m_options(std::make_shared<OptionMap>())
1407 { 1409 {
1408 } 1410 }
@@ -1435,6 +1437,20 @@ namespace cxxopts @@ -1435,6 +1437,20 @@ namespace cxxopts
1435 return *this; 1437 return *this;
1436 } 1438 }
1437 1439
  1440 + Options&
  1441 + set_width(size_t width)
  1442 + {
  1443 + m_width = width;
  1444 + return *this;
  1445 + }
  1446 +
  1447 + Options&
  1448 + set_tab_expansion(bool expansion=true)
  1449 + {
  1450 + m_tab_expansion = expansion;
  1451 + return *this;
  1452 + }
  1453 +
1438 ParseResult 1454 ParseResult
1439 parse(int argc, const char* const* argv); 1455 parse(int argc, const char* const* argv);
1440 1456
@@ -1519,6 +1535,8 @@ namespace cxxopts @@ -1519,6 +1535,8 @@ namespace cxxopts
1519 std::string m_positional_help{}; 1535 std::string m_positional_help{};
1520 bool m_show_positional; 1536 bool m_show_positional;
1521 bool m_allow_unrecognised; 1537 bool m_allow_unrecognised;
  1538 + size_t m_width;
  1539 + bool m_tab_expansion;
1522 1540
1523 std::shared_ptr<OptionMap> m_options; 1541 std::shared_ptr<OptionMap> m_options;
1524 std::vector<std::string> m_positional{}; 1542 std::vector<std::string> m_positional{};
@@ -1557,8 +1575,8 @@ namespace cxxopts @@ -1557,8 +1575,8 @@ namespace cxxopts
1557 1575
1558 namespace 1576 namespace
1559 { 1577 {
1560 - constexpr int OPTION_LONGEST = 30;  
1561 - constexpr int OPTION_DESC_GAP = 2; 1578 + constexpr size_t OPTION_LONGEST = 30;
  1579 + constexpr size_t OPTION_DESC_GAP = 2;
1562 1580
1563 std::basic_regex<char> option_matcher 1581 std::basic_regex<char> option_matcher
1564 ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); 1582 ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
@@ -1617,7 +1635,8 @@ namespace cxxopts @@ -1617,7 +1635,8 @@ namespace cxxopts
1617 ( 1635 (
1618 const HelpOptionDetails& o, 1636 const HelpOptionDetails& o,
1619 size_t start, 1637 size_t start,
1620 - size_t width 1638 + size_t allowed,
  1639 + bool m_tab_expansion
1621 ) 1640 )
1622 { 1641 {
1623 auto desc = o.desc; 1642 auto desc = o.desc;
@@ -1636,54 +1655,107 @@ namespace cxxopts @@ -1636,54 +1655,107 @@ namespace cxxopts
1636 1655
1637 String result; 1656 String result;
1638 1657
  1658 + if (m_tab_expansion)
  1659 + {
  1660 + String desc2;
  1661 + auto size = size_t{ 0 };
  1662 + for (auto c = std::begin(desc); c != std::end(desc); ++c)
  1663 + {
  1664 + if (*c == '\n')
  1665 + {
  1666 + desc2 += *c;
  1667 + size = 0;
  1668 + }
  1669 + else if (*c == '\t')
  1670 + {
  1671 + auto skip = 8 - size % 8;
  1672 + stringAppend(desc2, skip, ' ');
  1673 + size += skip;
  1674 + }
  1675 + else
  1676 + {
  1677 + desc2 += *c;
  1678 + ++size;
  1679 + }
  1680 + }
  1681 + desc = desc2;
  1682 + }
  1683 +
  1684 + desc += " ";
  1685 +
1639 auto current = std::begin(desc); 1686 auto current = std::begin(desc);
  1687 + auto previous = current;
1640 auto startLine = current; 1688 auto startLine = current;
1641 auto lastSpace = current; 1689 auto lastSpace = current;
1642 1690
1643 auto size = size_t{}; 1691 auto size = size_t{};
1644 1692
  1693 + bool appendNewLine;
  1694 + bool onlyWhiteSpace = true;
  1695 +
1645 while (current != std::end(desc)) 1696 while (current != std::end(desc))
1646 { 1697 {
1647 - if (*current == ' ') 1698 + appendNewLine = false;
  1699 +
  1700 + if (std::isblank(*previous))
1648 { 1701 {
1649 lastSpace = current; 1702 lastSpace = current;
1650 } 1703 }
1651 1704
1652 - if (*current == '\n') 1705 + if (!std::isblank(*current))
1653 { 1706 {
1654 - startLine = current + 1;  
1655 - lastSpace = startLine; 1707 + onlyWhiteSpace = false;
1656 } 1708 }
1657 - else if (size > width) 1709 +
  1710 + while (*current == '\n')
  1711 + {
  1712 + previous = current;
  1713 + ++current;
  1714 + appendNewLine = true;
  1715 + }
  1716 +
  1717 + if (!appendNewLine && size >= allowed)
1658 { 1718 {
1659 - if (lastSpace == startLine) 1719 + if (lastSpace != startLine)
1660 { 1720 {
1661 - stringAppend(result, startLine, current + 1);  
1662 - stringAppend(result, "\n");  
1663 - stringAppend(result, start, ' ');  
1664 - startLine = current + 1;  
1665 - lastSpace = startLine; 1721 + current = lastSpace;
  1722 + previous = current;
1666 } 1723 }
1667 - else 1724 + appendNewLine = true;
  1725 + }
  1726 +
  1727 + if (appendNewLine)
  1728 + {
  1729 + stringAppend(result, startLine, current);
  1730 + startLine = current;
  1731 + lastSpace = current;
  1732 +
  1733 + if (*previous != '\n')
1668 { 1734 {
1669 - stringAppend(result, startLine, lastSpace);  
1670 stringAppend(result, "\n"); 1735 stringAppend(result, "\n");
1671 - stringAppend(result, start, ' ');  
1672 - startLine = lastSpace + 1;  
1673 - lastSpace = startLine;  
1674 } 1736 }
  1737 +
  1738 + stringAppend(result, start, ' ');
  1739 +
  1740 + if (*previous != '\n')
  1741 + {
  1742 + stringAppend(result, lastSpace, current);
  1743 + }
  1744 +
  1745 + onlyWhiteSpace = true;
1675 size = 0; 1746 size = 0;
1676 } 1747 }
1677 - else  
1678 - {  
1679 - ++size;  
1680 - }  
1681 1748
  1749 + previous = current;
1682 ++current; 1750 ++current;
  1751 + ++size;
1683 } 1752 }
1684 1753
1685 - //append whatever is left  
1686 - stringAppend(result, startLine, current); 1754 + //append whatever is left but ignore whitespace
  1755 + if (!onlyWhiteSpace)
  1756 + {
  1757 + stringAppend(result, startLine, previous);
  1758 + }
1687 1759
1688 return result; 1760 return result;
1689 } 1761 }
@@ -2172,11 +2244,14 @@ Options::help_one_group(const std::string&amp; g) const @@ -2172,11 +2244,14 @@ Options::help_one_group(const std::string&amp; g) const
2172 longest = (std::max)(longest, stringLength(s)); 2244 longest = (std::max)(longest, stringLength(s));
2173 format.push_back(std::make_pair(s, String())); 2245 format.push_back(std::make_pair(s, String()));
2174 } 2246 }
  2247 + longest = (std::min)(longest, OPTION_LONGEST);
2175 2248
2176 - longest = (std::min)(longest, static_cast<size_t>(OPTION_LONGEST));  
2177 -  
2178 - //widest allowed description  
2179 - auto allowed = size_t{76} - longest - OPTION_DESC_GAP; 2249 + //widest allowed description -- min 10 chars for helptext/line
  2250 + size_t allowed = 10;
  2251 + if (m_width > allowed + longest + OPTION_DESC_GAP)
  2252 + {
  2253 + allowed = m_width - longest - OPTION_DESC_GAP;
  2254 + }
2180 2255
2181 auto fiter = format.begin(); 2256 auto fiter = format.begin();
2182 for (const auto& o : group->second.options) 2257 for (const auto& o : group->second.options)
@@ -2187,7 +2262,7 @@ Options::help_one_group(const std::string&amp; g) const @@ -2187,7 +2262,7 @@ Options::help_one_group(const std::string&amp; g) const
2187 continue; 2262 continue;
2188 } 2263 }
2189 2264
2190 - auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); 2265 + auto d = format_description(o, longest + OPTION_DESC_GAP, allowed, m_tab_expansion);
2191 2266
2192 result += fiter->first; 2267 result += fiter->first;
2193 if (stringLength(fiter->first) > longest) 2268 if (stringLength(fiter->first) > longest)