| 34 | } |
| 35 | |
| 36 | vector<string> wrapSingleLineString(const string& s, int lineLength, int hangingIndent) { |
| 37 | if (lineLength <= 0) throw std::invalid_argument("lineLength must be > 0."); |
| 38 | if (hangingIndent < 0) throw std::invalid_argument("hangingIndent must be >= 0."); |
| 39 | if (hangingIndent >= lineLength) throw std::invalid_argument("hangingIndent must be < lineLength."); |
| 40 | if (s.find('\t') != std::string::npos) throw std::invalid_argument("s must not contain tabs."); |
| 41 | if (s.find('\n') != std::string::npos) throw std::invalid_argument("s must not contain line breaks."); |
| 42 | |
| 43 | vector<string> lines; |
| 44 | auto p = &s[0]; |
| 45 | auto lineBegin = p; |
| 46 | auto lineEnd = p; |
| 47 | const auto end = p + s.size(); |
| 48 | // Iterate over input string |
| 49 | while (p <= end) { |
| 50 | // If we're at a word boundary: update lineEnd |
| 51 | if (p == end || *p == ' ' || *p == '|') { |
| 52 | lineEnd = p; |
| 53 | } |
| 54 | |
| 55 | // If we've hit lineLength or the end of the string: add a new result line |
| 56 | const int currentIndent = lines.empty() ? 0 : hangingIndent; |
| 57 | if (p == end || p - lineBegin == lineLength - currentIndent) { |
| 58 | if (lineEnd == lineBegin) { |
| 59 | // The line contains a single word, which is too long. Split mid-word. |
| 60 | lineEnd = p; |
| 61 | } |
| 62 | |
| 63 | // Add trimmed line to list |
| 64 | string line(lineBegin, lineEnd); |
| 65 | boost::algorithm::trim_right(line); |
| 66 | lines.push_back(string(currentIndent, ' ') + line); |
| 67 | |
| 68 | // Resume after the last line, skipping spaces |
| 69 | p = lineEnd; |
| 70 | while (p != end && *p == ' ') ++p; |
| 71 | lineBegin = lineEnd = p; |
| 72 | } |
| 73 | |
| 74 | ++p; |
| 75 | } |
| 76 | |
| 77 | return lines; |
| 78 | } |
| 79 | |
| 80 | vector<string> wrapString(const string& s, int lineLength, int hangingIndent) { |
| 81 | vector<string> lines; |