From b0a7bdf6b2ad6255b4ea843703b77aed040718d3 Mon Sep 17 00:00:00 2001 From: Yedidya Feldblum Date: Tue, 9 Feb 2016 16:28:21 -0800 Subject: [PATCH] stripLeftMargin Summary: [Folly] `stripLeftMargin`. So you can do: TEST(MyClass, doSomethingWithString) { // Multiline string literal is indented properly inside the test case // instead of being aligned all the way to the left, which would make // the test case read poorly. auto input = folly::stripLeftMargin(R"TEXT( first line second line third line )TEXT"); auto expected = //... auto actual = MyClass::doSomethingWithString(input); EXPECT_EQ(expected, actual); } Reviewed By: markisaa Differential Revision: D2909736 fb-gh-sync-id: ebb07da05e1a788535064cfcd9e07f617a007800 shipit-source-id: ebb07da05e1a788535064cfcd9e07f617a007800 --- folly/String.cpp | 22 +++++++- folly/String.h | 13 ++++- folly/test/StringTest.cpp | 109 +++++++++++++++++++++++++++++++++++++- 3 files changed, 141 insertions(+), 3 deletions(-) diff --git a/folly/String.cpp b/folly/String.cpp index 5042cec9..74ccb4ec 100644 --- a/folly/String.cpp +++ b/folly/String.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2015 Facebook, Inc. + * Copyright 2016 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ #include +#include #include #include @@ -548,6 +549,25 @@ size_t hexDumpLine(const void* ptr, size_t offset, size_t size, } // 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, ""); + const auto sentinel = std::numeric_limits::max(); + auto indent = sentinel; + sregex_iterator it(s.cbegin(), s.cend(), kScan); + sregex_iterator itend; + for (; it != itend; ++it) { + indent = std::min(indent, it->length()); + } + indent = indent == sentinel ? 0 : indent; + s = regex_replace(s, regex(sformat(R"(^[ \t]{{0,{0}}})", indent)), ""); + return s; +} + } // namespace folly #ifdef FOLLY_DEFINED_DMGL diff --git a/folly/String.h b/folly/String.h index f9ec5fd5..19a2c3b2 100644 --- a/folly/String.h +++ b/folly/String.h @@ -1,5 +1,5 @@ /* - * Copyright 2015 Facebook, Inc. + * Copyright 2016 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -580,6 +580,17 @@ inline StringPiece skipWhitespace(StringPiece sp) { return ltrimWhitespace(sp); } +/** + * Strips the leading and the trailing whitespace-only lines. Then looks for + * the least indented non-whitespace-only line and removes its amount of + * leading whitespace from every line. Assumes leading whitespace is either all + * spaces or all tabs. + * + * Purpose: including a multiline string literal in source code, indented to + * the level expected from context. + */ +std::string stripLeftMargin(std::string s); + /** * Fast, in-place lowercasing of ASCII alphabetic characters in strings. * Leaves all other characters unchanged, including those with the 0x80 diff --git a/folly/test/StringTest.cpp b/folly/test/StringTest.cpp index 759c4477..f687213f 100644 --- a/folly/test/StringTest.cpp +++ b/folly/test/StringTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2015 Facebook, Inc. + * Copyright 2016 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ #include +#include #include using namespace folly; @@ -1131,6 +1132,112 @@ TEST(String, whitespace) { EXPECT_EQ("", rtrimWhitespace("\r ")); } +TEST(String, stripLeftMargin_empty) { + auto input = R"TEXT( + )TEXT"; + auto expected = ""; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_one_line) { + auto input = R"TEXT( + hi there bob! + )TEXT"; + auto expected = "hi there bob!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_two_lines) { + auto input = R"TEXT( + hi there bob! + nice weather today! + )TEXT"; + auto expected = "hi there bob!\nnice weather today!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_three_lines_uneven) { + auto input = R"TEXT( + hi there bob! + nice weather today! + so long! + )TEXT"; + auto expected = " hi there bob!\nnice weather today!\n so long!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_preceding_blank_lines) { + auto input = R"TEXT( + + + hi there bob! + )TEXT"; + auto expected = "\n\nhi there bob!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_succeeding_blank_lines) { + auto input = R"TEXT( + hi there bob! + + + )TEXT"; + auto expected = "hi there bob!\n\n\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_interstitial_undented_whiteline) { + // 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!\n ", input); + auto expected = "hi there bob!\n\nso long!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_interstitial_dedented_whiteline) { + // 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!\n ", input); + auto expected = "hi there bob!\n\nso long!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_interstitial_equidented_whiteline) { + // 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!\n ", input); + auto expected = "hi there bob!\n\nso long!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + +TEST(String, stripLeftMargin_interstitial_indented_whiteline) { + // 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!\n ", input); + auto expected = "hi there bob!\n \nso long!\n"; + EXPECT_EQ(expected, stripLeftMargin(input)); +} + const folly::StringPiece kTestUTF8 = "This is \U0001F602 stuff!"; TEST(UTF8StringPiece, valid_utf8) { -- 2.34.1