#include <folly/String.h>
-#include <boost/regex.hpp>
#include <folly/Format.h>
#include <folly/ScopeGuard.h>
} // namespace detail
std::string stripLeftMargin(std::string s) {
- using namespace boost;
- static const auto kPre = regex(R"(\A[ \t]*\n)");
- static const auto kPost = regex(R"([ \t]+\z)");
- static const auto kScan = regex(R"(^[ \t]*(?=\S))");
- s = regex_replace(s, kPre, "");
- s = regex_replace(s, kPost, "");
+ std::vector<StringPiece> pieces;
+ split("\n", s, pieces);
+ auto piecer = range(pieces);
+
+ auto piece = (piecer.end() - 1);
+ auto needle = std::find_if(piece->begin(),
+ piece->end(),
+ [](char c) { return c != ' ' && c != '\t'; });
+ if (needle == piece->end()) {
+ (piecer.end() - 1)->clear();
+ }
+ piece = piecer.begin();
+ needle = std::find_if(piece->begin(),
+ piece->end(),
+ [](char c) { return c != ' ' && c != '\t'; });
+ if (needle == piece->end()) {
+ piecer.erase(piecer.begin(), piecer.begin() + 1);
+ }
+
const auto sentinel = std::numeric_limits<size_t>::max();
auto indent = sentinel;
- sregex_iterator it(s.cbegin(), s.cend(), kScan);
- sregex_iterator itend;
- for (; it != itend; ++it) {
- indent = std::min<size_t>(indent, it->length());
+ size_t max_length = 0;
+ for (auto piece = piecer.begin(); piece != piecer.end(); piece++) {
+ needle = std::find_if(piece->begin(),
+ piece->end(),
+ [](char c) { return c != ' ' && c != '\t'; });
+ if (needle != piece->end()) {
+ indent = std::min<size_t>(indent, needle - piece->begin());
+ } else {
+ max_length = std::max<size_t>(piece->size(), max_length);
+ }
+ }
+ indent = indent == sentinel ? max_length : indent;
+ for (auto& piece : piecer) {
+ if (piece.size() < indent) {
+ piece.clear();
+ } else {
+ piece.erase(piece.begin(), piece.begin() + indent);
+ }
}
- indent = indent == sentinel ? 0 : indent;
- s = regex_replace(s, regex(sformat(R"(^[ \t]{{0,{0}}})", indent)), "");
- return s;
+ return join("\n", piecer);
}
} // namespace folly
EXPECT_EQ("", rtrimWhitespace("\r "));
}
+TEST(String, stripLeftMargin_really_empty) {
+ auto input = "";
+ auto expected = "";
+ EXPECT_EQ(expected, stripLeftMargin(input));
+}
+
TEST(String, stripLeftMargin_empty) {
auto input = R"TEXT(
)TEXT";
EXPECT_EQ(expected, stripLeftMargin(input));
}
+TEST(String, stripLeftMargin_only_whitespace) {
+ // using ~ as a marker
+ string input = R"TEXT(
+ ~
+ )TEXT";
+ input = boost::regex_replace(input, boost::regex("~"), "");
+ EXPECT_EQ("\n \n ", input);
+ auto expected = "\n";
+ EXPECT_EQ(expected, stripLeftMargin(input));
+}
+
+TEST(String, stripLeftMargin_only_uneven_whitespace) {
+ // using ~ as a marker1
+ string input = R"TEXT(
+ ~
+ ~
+ )TEXT";
+ input = boost::regex_replace(input, boost::regex("~"), "");
+ EXPECT_EQ("\n \n \n ", input);
+ auto expected = "\n\n";
+
+ EXPECT_EQ(expected, stripLeftMargin(input));
+}
+
TEST(String, stripLeftMargin_one_line) {
auto input = R"TEXT(
hi there bob!
EXPECT_EQ(expected, stripLeftMargin(input));
}
+TEST(String, stripLeftMargin_no_pre_whitespace) {
+ // using ~ as a marker
+ string input = R"TEXT( hi there bob!
+ ~
+ so long!
+ )TEXT";
+ input = boost::regex_replace(input, boost::regex("~"), "");
+ EXPECT_EQ(" hi there bob!\n \n so long!\n ", input);
+ auto expected = "hi there bob!\n \nso long!\n";
+ EXPECT_EQ(expected, stripLeftMargin(input));
+}
+
+TEST(String, stripLeftMargin_no_post_whitespace) {
+ // using ~ as a marker
+ string input = R"TEXT(
+ hi there bob!
+ ~
+ so long! )TEXT";
+ input = boost::regex_replace(input, boost::regex("~"), "");
+ EXPECT_EQ("\n hi there bob!\n \n so long! ", input);
+ auto expected = "hi there bob!\n \nso long! ";
+ EXPECT_EQ(expected, stripLeftMargin(input));
+}
+
const folly::StringPiece kTestUTF8 = "This is \U0001F602 stuff!";
TEST(UTF8StringPiece, valid_utf8) {