Skip to content

Commit e7aacf2

Browse files
committed
Add function wrap_line() to wrap strings at given width
1 parent 3f9026e commit e7aacf2

File tree

4 files changed

+145
-0
lines changed

4 files changed

+145
-0
lines changed

src/util/string_utils.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,75 @@ std::string capitalize(const std::string &str)
174174
capitalized[0] = toupper(capitalized[0]);
175175
return capitalized;
176176
}
177+
178+
std::string wrap_line(
179+
const std::string &line,
180+
const std::size_t left_margin,
181+
const std::size_t width)
182+
{
183+
return wrap_line(line.cbegin(), line.cend(), left_margin, width);
184+
}
185+
186+
std::string wrap_line(
187+
std::string::const_iterator left,
188+
std::string::const_iterator right,
189+
const std::size_t left_margin,
190+
const std::size_t width)
191+
{
192+
PRECONDITION(left_margin < width);
193+
194+
const std::size_t column_width = width - left_margin;
195+
const std::string margin(left_margin, ' ');
196+
197+
auto distance = std::distance(left, right);
198+
CHECK_RETURN(distance > 0);
199+
200+
std::string result;
201+
202+
if(static_cast<std::size_t>(distance) <= column_width)
203+
{
204+
result.append(margin);
205+
result.append(left, right);
206+
207+
return result;
208+
}
209+
210+
auto it_line_begin = left;
211+
212+
do
213+
{
214+
// points to the first character past the current column
215+
auto it = it_line_begin + column_width;
216+
217+
auto rit_r = std::reverse_iterator<decltype(it)>(it) - 1;
218+
auto rit_l = rit_r + column_width;
219+
220+
auto rit_space = std::find(rit_r, rit_l, ' ');
221+
222+
if(rit_space != rit_l)
223+
{
224+
auto it_space = rit_space.base() - 1;
225+
CHECK_RETURN(*it_space == ' ');
226+
227+
result.append(margin);
228+
result.append(it_line_begin, it_space);
229+
result.append("\n");
230+
231+
it_line_begin = it_space + 1;
232+
}
233+
else
234+
{
235+
// we have not found a space, thus cannot wrap this line
236+
result.clear();
237+
result.append(left, right);
238+
239+
return result;
240+
}
241+
} while(static_cast<std::size_t>(std::distance(it_line_begin, right)) >
242+
column_width);
243+
244+
result.append(margin);
245+
result.append(it_line_begin, right);
246+
247+
return result;
248+
}

src/util/string_utils.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,44 @@ std::string escape(const std::string &);
108108
/// \return string with non-alphanumeric characters escaped
109109
std::string escape_non_alnum(const std::string &to_escape);
110110

111+
/// Wrap line at spaces to not extend past the right margin, and include given
112+
/// padding with spaces to the left
113+
///
114+
/// The given string should not contain any newlines.
115+
///
116+
/// \param line: line to wrap, should not contain newlines
117+
/// \param left_margin: each line will be padded to the left with `left_margin`
118+
/// spaces
119+
/// \param width: width of the resulting text, i.e., right margin
120+
/// \return resulting string such that each line has length less or equal to
121+
/// `width`, and each line includes `left_margin` spaces to the left; if the
122+
/// given line cannot be wrapped (i.e., it cannot be broken up at spaces such
123+
/// that every resulting line fits within the margin), the given line is
124+
/// returned unchanged
125+
std::string wrap_line(
126+
const std::string &line,
127+
const std::size_t left_margin = 0,
128+
const std::size_t width = 80);
129+
130+
/// Wrap line at spaces to not extend past the right margin, and include given
131+
/// padding with spaces to the left
132+
///
133+
/// The given string should not contain any newlines.
134+
///
135+
/// \param left: iterator to beginning of string
136+
/// \param right: iterator to end of string
137+
/// \param left_margin: each line will be padded to the left with `left_margin`
138+
/// spaces
139+
/// \param width: width of the resulting text, i.e., right margin
140+
/// \return resulting string such that each line has length less or equal to
141+
/// `width`, and each line includes `left_margin` spaces to the left; if the
142+
/// given line cannot be wrapped (i.e., it cannot be broken up at spaces such
143+
/// that every resulting line fits within the margin), the given line is
144+
/// returned unchanged
145+
std::string wrap_line(
146+
const std::string::const_iterator left,
147+
const std::string::const_iterator right,
148+
const std::size_t left_margin = 0,
149+
const std::size_t width = 80);
150+
111151
#endif

unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ SRC += analyses/ai/ai.cpp \
117117
util/string_utils/join_string.cpp \
118118
util/string_utils/split_string.cpp \
119119
util/string_utils/strip_string.cpp \
120+
util/string_utils/wrap_line.cpp \
120121
util/symbol_table.cpp \
121122
util/symbol.cpp \
122123
util/unicode.cpp \
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/// Author: Daniel Poetzl
2+
3+
/// \file Tests for wrap_line()
4+
5+
#include <testing-utils/use_catch.h>
6+
#include <util/string_utils.h>
7+
8+
TEST_CASE("Wrap line", "[core][util]")
9+
{
10+
SECTION("Wrap without margin")
11+
{
12+
REQUIRE(wrap_line("x", 0, 1) == "x");
13+
REQUIRE(wrap_line("x x", 0, 1) == "x\nx");
14+
REQUIRE(wrap_line("x x x", 0, 1) == "x\nx\nx");
15+
16+
REQUIRE(wrap_line("x x", 0, 2) == "x\nx");
17+
REQUIRE(wrap_line("xx xx xx", 0, 4) == "xx\nxx\nxx");
18+
19+
REQUIRE(wrap_line("xx", 0, 1) == "xx");
20+
}
21+
22+
SECTION("Wrap with margin")
23+
{
24+
REQUIRE(wrap_line("x", 1, 2) == " x");
25+
REQUIRE(wrap_line("x x", 1, 2) == " x\n x");
26+
REQUIRE(wrap_line("x x x", 1, 2) == " x\n x\n x");
27+
28+
REQUIRE(wrap_line("x", 2, 3) == " x");
29+
30+
REQUIRE(wrap_line("xx", 1, 2) == "xx");
31+
}
32+
}

0 commit comments

Comments
 (0)