aboutsummaryrefslogtreecommitdiff
path: root/args.hxx
diff options
context:
space:
mode:
authorPavel Belikov <pavel.fuchs.belikov@gmail.com>2017-12-02 14:08:05 +0300
committerPavel Belikov <pavel.fuchs.belikov@gmail.com>2017-12-02 14:08:05 +0300
commit6ac2747212532f5296b08b37516badb5a79e89a9 (patch)
tree179fb89c33a635bf66f410b6fcb2e878d97f04fa /args.hxx
parentMerge pull request #48 from pavel-belikov/fix-multiple-inclusion (diff)
downloadargs.hxx-6ac2747212532f5296b08b37516badb5a79e89a9.tar.xz
fix usage line wrapping
Diffstat (limited to 'args.hxx')
-rw-r--r--args.hxx154
1 files changed, 112 insertions, 42 deletions
diff --git a/args.hxx b/args.hxx
index 03faade..a8818ab 100644
--- a/args.hxx
+++ b/args.hxx
@@ -30,6 +30,7 @@
#define ARGS_HXX
#include <algorithm>
+#include <iterator>
#include <exception>
#include <functional>
#include <sstream>
@@ -83,54 +84,56 @@ namespace args
return length;
}
- /** (INTERNAL) Wrap a string into a vector of lines
+ /** (INTERNAL) Wrap a vector of words into a vector of lines
*
- * This is quick and hacky, but works well enough. You can specify a
- * different width for the first line
+ * Empty words are skipped. Word "\n" forces wrapping.
*
+ * \param begin The begin iterator
+ * \param end The end iterator
* \param width The width of the body
- * \param the width of the first line, defaults to the width of the body
+ * \param firstlinewidth the width of the first line, defaults to the width of the body
+ * \param firstlineindent the indent of the first line, defaults to 0
* \return the vector of lines
*/
- inline std::vector<std::string> Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0)
+ template <typename It>
+ inline std::vector<std::string> Wrap(It begin,
+ It end,
+ const std::string::size_type width,
+ std::string::size_type firstlinewidth = 0,
+ std::string::size_type firstlineindent = 0)
{
- // Preserve existing line breaks
- const auto newlineloc = in.find('\n');
- if (newlineloc != in.npos)
- {
- auto first = Wrap(std::string(in, 0, newlineloc), width);
- auto second = Wrap(std::string(in, newlineloc + 1), width);
- first.insert(
- std::end(first),
- std::make_move_iterator(std::begin(second)),
- std::make_move_iterator(std::end(second)));
- return first;
- }
+ std::vector<std::string> output;
+ std::string line(firstlineindent, ' ');
+ bool empty = true;
+
if (firstlinewidth == 0)
{
firstlinewidth = width;
}
- auto currentwidth = firstlinewidth;
- std::istringstream stream(in);
- std::vector<std::string> output;
- std::string line;
- bool empty = true;
+ auto currentwidth = firstlinewidth;
- for (char c : in)
+ for (auto it = begin; it != end; ++it)
{
- if (!isspace(c))
+ if (it->empty())
{
- break;
+ continue;
}
- line += c;
- }
- while (stream)
- {
- std::string item;
- stream >> item;
- auto itemsize = Glyphs(item);
+ if (*it == "\n")
+ {
+ if (!empty)
+ {
+ output.push_back(line);
+ line.clear();
+ empty = true;
+ currentwidth = width;
+ }
+
+ continue;
+ }
+
+ auto itemsize = Glyphs(*it);
if ((line.length() + 1 + itemsize) > currentwidth)
{
if (!empty)
@@ -141,6 +144,7 @@ namespace args
currentwidth = width;
}
}
+
if (itemsize > 0)
{
if (!empty)
@@ -148,7 +152,7 @@ namespace args
line += ' ';
}
- line += item;
+ line += *it;
empty = false;
}
}
@@ -157,9 +161,50 @@ namespace args
{
output.push_back(line);
}
+
return output;
}
+ /** (INTERNAL) Wrap a string into a vector of lines
+ *
+ * This is quick and hacky, but works well enough. You can specify a
+ * different width for the first line
+ *
+ * \param width The width of the body
+ * \param firstlinewid the width of the first line, defaults to the width of the body
+ * \return the vector of lines
+ */
+ inline std::vector<std::string> Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0)
+ {
+ // Preserve existing line breaks
+ const auto newlineloc = in.find('\n');
+ if (newlineloc != in.npos)
+ {
+ auto first = Wrap(std::string(in, 0, newlineloc), width);
+ auto second = Wrap(std::string(in, newlineloc + 1), width);
+ first.insert(
+ std::end(first),
+ std::make_move_iterator(std::begin(second)),
+ std::make_move_iterator(std::end(second)));
+ return first;
+ }
+
+ std::istringstream stream(in);
+ std::string::size_type indent = 0;
+
+ for (char c : in)
+ {
+ if (!isspace(c))
+ {
+ break;
+ }
+ ++indent;
+ }
+
+ return Wrap(std::istream_iterator<std::string>(stream), std::istream_iterator<std::string>(),
+ width, firstlinewidth, indent);
+ }
+
#ifdef ARGS_NOEXCEPT
/// Error class, for when ARGS_NOEXCEPT is defined
enum class Error
@@ -1748,7 +1793,32 @@ namespace args
if (!ProglinePostfix().empty())
{
- res.push_back(ProglinePostfix());
+ std::string line;
+ for (char c : ProglinePostfix())
+ {
+ if (isspace(c))
+ {
+ if (!line.empty())
+ {
+ res.push_back(line);
+ line.clear();
+ }
+
+ if (c == '\n')
+ {
+ res.push_back("\n");
+ }
+ }
+ else
+ {
+ line += c;
+ }
+ }
+
+ if (!line.empty())
+ {
+ res.push_back(line);
+ }
}
return res;
@@ -2387,15 +2457,15 @@ namespace args
const bool hasoptions = command.HasFlag();
const bool hasarguments = command.HasPositional();
- std::ostringstream prognameline;
- prognameline << helpParams.usageString << Prog();
-
- for (const std::string &posname: command.GetProgramLine(helpParams))
- {
- prognameline << ' ' << posname;
- }
+ std::vector<std::string> prognameline;
+ prognameline.push_back(helpParams.usageString);
+ prognameline.push_back(Prog());
+ auto commandProgLine = command.GetProgramLine(helpParams);
+ prognameline.insert(prognameline.end(), commandProgLine.begin(), commandProgLine.end());
- const auto proglines = Wrap(prognameline.str(), helpParams.width - (helpParams.progindent + 4), helpParams.width - helpParams.progindent);
+ const auto proglines = Wrap(prognameline.begin(), prognameline.end(),
+ helpParams.width - (helpParams.progindent + helpParams.progtailindent),
+ helpParams.width - helpParams.progindent);
auto progit = std::begin(proglines);
if (progit != std::end(proglines))
{