More features for TextLineWrapper.
authorTeemu Murtola <teemu.murtola@gmail.com>
Mon, 27 Aug 2012 17:53:06 +0000 (20:53 +0300)
committerTeemu Murtola <teemu.murtola@gmail.com>
Fri, 7 Sep 2012 04:11:45 +0000 (07:11 +0300)
Add more control over formatting the wrapped lines:
- Add possibility to indent the wrapped lines.
- Add possibility to indent the first line after an explicit line break
  differently from other lines.
- Add possibility to mark line continuation using an explicit character.
- Add possibility to either remove or keep leading whitespace after an
  explicit line break.
All except the last are already useful for the current console output.
Depending on the chosen solution, some or all of these are also needed
for #969.

Also expose the lower-level interface used internally that makes it
possible to write code that iterates over the wrapped lines and does
something else than just format them into a buffer.  May be useful for
some cases in #969, but is necessary to use the wrapper for #838 (line
wrapping in error messages).

Change-Id: I905ba29856773656bf000c4b2e14d1ed2ba4de31

src/gromacs/commandline/cmdlinehelpwriter.cpp
src/gromacs/onlinehelp/helpformat.cpp
src/gromacs/onlinehelp/helptopic.h
src/gromacs/onlinehelp/helpwritercontext.cpp
src/gromacs/utility/stringutil.cpp
src/gromacs/utility/stringutil.h
src/gromacs/utility/tests/refdata/TextLineWrapperTest_HandlesContinuationCharacter.xml [new file with mode: 0644]
src/gromacs/utility/tests/refdata/TextLineWrapperTest_HandlesHangingIndent.xml [new file with mode: 0644]
src/gromacs/utility/tests/refdata/TextLineWrapperTest_HandlesIndent.xml [new file with mode: 0644]
src/gromacs/utility/tests/refdata/TextLineWrapperTest_WrapsCorrectlyWithExtraWhitespace.xml
src/gromacs/utility/tests/stringutil.cpp

index b3c032a32f37f88aa2548a5bc6135ce561603126..89e6d29a9455deb42ed460a2a4506f0c7b805216 100644 (file)
@@ -432,13 +432,17 @@ void OptionsConsoleFormatter::formatSelectionOption(
     selectionOptionFormatter_.addColumnLine(1, context.substituteMarkup(option.description()));
     file.writeString(selectionOptionFormatter_.formatRow());
 
+    TextLineWrapper wrapper;
+    wrapper.settings().setLineLength(77);
+    wrapper.settings().setFirstLineIndent(4);
+    wrapper.settings().setIndent(8);
+    wrapper.settings().setContinuationChar('\\');
     // TODO: What to do with selection variables?
     // They are not printed as values for any option.
     for (int i = 0; i < option.valueCount(); ++i)
     {
         std::string value(option.formatValue(i));
-        // TODO: Wrapping
-        file.writeLine(formatString("    %s", value.c_str()));
+        file.writeLine(wrapper.wrapToString(value));
     }
 }
 
index 61d8bb9447f5ec42adcb430103e675a190c91960..688f7fa7f38fb4a098d716510cdf23e2c1a24d9e 100644 (file)
@@ -211,7 +211,7 @@ void TextTableFormatter::addColumnLine(int index, const std::string &text)
     TextLineWrapper wrapper;
     if (column.bWrap_)
     {
-        wrapper.setLineLength(column.width());
+        wrapper.settings().setLineLength(column.width());
     }
     std::vector<std::string> lines(wrapper.wrapToVector(text));
     column.lines_.insert(column.lines_.end(), lines.begin(), lines.end());
index 5f1a5098b3786927cde43055556adbcd172fce0b..bd3f1d5dc25d9e6e5a176de612136330de38b6d2 100644 (file)
@@ -40,6 +40,7 @@
 #ifndef GMX_ONLINEHELP_HELPTOPIC_H
 #define GMX_ONLINEHELP_HELPTOPIC_H
 
+#include "../utility/common.h"
 #include "../utility/stringutil.h"
 #include "../utility/uniqueptr.h"
 
index 977ddfcc771b829f20519f69e1d96f709d12b78f..eca27cbc84dc6467355098340b8b7d070ee3dd36 100644 (file)
@@ -144,7 +144,7 @@ void HelpWriterContext::writeTitle(const std::string &title) const
 void HelpWriterContext::writeTextBlock(const std::string &text) const
 {
     TextLineWrapper wrapper;
-    wrapper.setLineLength(78);
+    wrapper.settings().setLineLength(78);
     const char *program = ProgramInfo::getInstance().programName().c_str();
     std::string newText = replaceAll(text, "[PROGRAM]", program);
     outputFile().writeLine(wrapper.wrapToString(substituteMarkup(newText)));
index a0cf5c13ab56743bf226de63d5a6bdc19e24e8c3..61ba3a0c37194d704f4d2c7f5b5e5c4b5dc59d2f 100644 (file)
@@ -177,71 +177,83 @@ replaceAllWords(const std::string &input, const char *from, const char *to)
     return replaceInternal(input, from, to, true);
 }
 
+
 /********************************************************************
- * TextLineWrapper::Impl
+ * TextLineWrapperSettings
  */
 
-/*! \internal \brief
- * Private implementation class for TextLineWrapper.
- *
- * \ingroup module_utility
- */
-class TextLineWrapper::Impl
+TextLineWrapperSettings::TextLineWrapperSettings()
+    : maxLength_(0), indent_(0), firstLineIndent_(-1),
+      bStripLeadingWhitespace_(true), continuationChar_('\0')
 {
-    public:
-        //! Initialize default values for the wrapper.
-        Impl() : maxLength_(0) {}
+}
 
-        /*! \brief
-         * Helper method to find the next wrapped line.
-         *
-         * \param[in]     input         Full input string.
-         * \param[in,out] lineStartPtr
-         *      Index of first character for the line to wrap.
-         *      On exit, index of the first character of the next line.
-         * \returns   Next output line.
-         */
-        std::string wrapNextLine(const std::string &input,
-                                 size_t *lineStartPtr) const;
 
-        //! Maximum length of output lines, or <= 0 if no limit.
-        int                     maxLength_;
-};
+/********************************************************************
+ * TextLineWrapper
+ */
 
-std::string
-TextLineWrapper::Impl::wrapNextLine(const std::string &input,
-                                    size_t *lineStartPtr) const
+size_t
+TextLineWrapper::findNextLine(const char *input, size_t lineStart) const
 {
-    // Strip leading whitespace.
-    size_t lineStart = input.find_first_not_of(' ', *lineStartPtr);
-    if (lineStart == std::string::npos)
+    size_t inputLength = std::strlen(input);
+    bool bFirstLine = (lineStart == 0 || input[lineStart - 1] == '\n');
+    // Ignore leading whitespace if necessary.
+    if (!bFirstLine || settings_.bStripLeadingWhitespace_)
     {
-        *lineStartPtr = lineStart;
-        return std::string();
+        lineStart += std::strspn(input + lineStart, " ");
+        if (lineStart >= inputLength)
+        {
+            return inputLength;
+        }
     }
 
-    size_t lineEnd = std::string::npos;
-    size_t nextNewline
-        = std::min(input.find('\n', lineStart), input.length());
-    if (maxLength_ <= 0 || nextNewline <= lineStart + maxLength_)
-    {
-        lineEnd = nextNewline;
-    }
-    else
+    int indent = (bFirstLine ? settings_.firstLineIndent() : settings_.indent());
+    size_t lastAllowedBreakPoint
+        = (settings_.lineLength() > 0
+           ? std::min(lineStart + settings_.lineLength() - indent, inputLength)
+           : inputLength);
+    // Ignore trailing whitespace.
+    lastAllowedBreakPoint += std::strspn(input + lastAllowedBreakPoint, " ");
+    size_t lineEnd = lineStart;
+    do
     {
-        size_t bestSpace = input.rfind(' ', lineStart + maxLength_);
-        if (bestSpace < lineStart || bestSpace == std::string::npos)
+        const char *nextBreakPtr = std::strpbrk(input + lineEnd, " \n");
+        size_t nextBreak
+            = (nextBreakPtr != NULL ? nextBreakPtr - input : inputLength);
+        if (nextBreak > lastAllowedBreakPoint && lineEnd > lineStart)
         {
-            bestSpace = input.find(' ', lineStart);
+            break;
         }
-        lineEnd = std::min(bestSpace, nextNewline);
+        lineEnd = nextBreak + 1;
     }
+    while (lineEnd < lastAllowedBreakPoint && input[lineEnd - 1] != '\n');
+    return (lineEnd < inputLength ? lineEnd : inputLength);
+}
+
+size_t
+TextLineWrapper::findNextLine(const std::string &input, size_t lineStart) const
+{
+    return findNextLine(input.c_str(), lineStart);
+}
 
-    if (lineEnd == std::string::npos)
+std::string
+TextLineWrapper::formatLine(const std::string &input,
+                            size_t lineStart, size_t lineEnd) const
+{
+    size_t inputLength = input.length();
+    bool bFirstLine = (lineStart == 0 || input[lineStart - 1] == '\n');
+    // Strip leading whitespace if necessary.
+    if (!bFirstLine || settings_.bStripLeadingWhitespace_)
     {
-        lineEnd = input.length();
+        lineStart = input.find_first_not_of(' ', lineStart);
+        if (lineStart >= inputLength)
+        {
+            return std::string();
+        }
     }
-    *lineStartPtr = lineEnd + 1;
+    int indent = (bFirstLine ? settings_.firstLineIndent() : settings_.indent());
+    bool bContinuation = (lineEnd < inputLength && input[lineEnd - 1] != '\n');
     // Strip trailing whitespace.
     while (lineEnd > lineStart && std::isspace(input[lineEnd - 1]))
     {
@@ -249,26 +261,14 @@ TextLineWrapper::Impl::wrapNextLine(const std::string &input,
     }
 
     size_t lineLength = lineEnd - lineStart;
-    return input.substr(lineStart, lineLength);
-}
-
-/********************************************************************
- * TextLineWrapper
- */
-
-TextLineWrapper::TextLineWrapper()
-    : impl_(new Impl)
-{
-}
-
-TextLineWrapper::~TextLineWrapper()
-{
-}
-
-TextLineWrapper &TextLineWrapper::setLineLength(int length)
-{
-    impl_->maxLength_ = length;
-    return *this;
+    std::string result(indent, ' ');
+    result.append(input, lineStart, lineLength);
+    if (bContinuation && settings_.continuationChar_ != '\0')
+    {
+        result.append(1, ' ');
+        result.append(1, settings_.continuationChar_);
+    }
+    return result;
 }
 
 std::string
@@ -279,12 +279,14 @@ TextLineWrapper::wrapToString(const std::string &input) const
     size_t length = input.length();
     while (lineStart < length)
     {
-        result.append(impl_->wrapNextLine(input, &lineStart));
-        if (lineStart < length
-            || (lineStart == length && input[length - 1] == '\n'))
+        size_t nextLineStart = findNextLine(input, lineStart);
+        result.append(formatLine(input, lineStart, nextLineStart));
+        if (nextLineStart < length
+            || (nextLineStart == length && input[length - 1] == '\n'))
         {
             result.append("\n");
         }
+        lineStart = nextLineStart;
     }
     return result;
 }
@@ -294,9 +296,12 @@ TextLineWrapper::wrapToVector(const std::string &input) const
 {
     std::vector<std::string> result;
     size_t lineStart = 0;
-    while (lineStart < input.length())
+    size_t length = input.length();
+    while (lineStart < length)
     {
-        result.push_back(impl_->wrapNextLine(input, &lineStart));
+        size_t nextLineStart = findNextLine(input, lineStart);
+        result.push_back(formatLine(input, lineStart, nextLineStart));
+        lineStart = nextLineStart;
     }
     return result;
 }
index f6bb5c917409a4cd4b8973b01872079f9c88402b..dc6b7a8c4a593679832e0ca9456c3a1560fff49c 100644 (file)
@@ -42,8 +42,6 @@
 #include <string>
 #include <vector>
 
-#include "common.h"
-
 namespace gmx
 {
 
@@ -181,6 +179,126 @@ std::string replaceAll(const std::string &input,
 std::string replaceAllWords(const std::string &input,
                             const char *from, const char *to);
 
+class TextLineWrapper;
+
+/*! \brief
+ * Stores settings for line wrapping.
+ *
+ * Methods in this class do not throw.
+ *
+ * \see TextLineWrapper
+ *
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+class TextLineWrapperSettings
+{
+    public:
+        /*! \brief
+         * Initializes default wrapper settings.
+         *
+         * Default settings are:
+         *  - No maximum line width (only explicit line breaks).
+         *  - No indentation.
+         *  - No continuation characters.
+         *  - Ignore whitespace after an explicit newline.
+         */
+        TextLineWrapperSettings();
+
+        /*! \brief
+         * Sets the maximum length for output lines.
+         *
+         * \param[in] length  Maximum length for the lines after wrapping.
+         *
+         * If this method is not called, or is called with zero \p length, the
+         * wrapper has no maximum length (only wraps at explicit line breaks).
+         */
+        void setLineLength(int length) { maxLength_ = length; }
+        /*! \brief
+         * Sets the indentation for output lines.
+         *
+         * \param[in] indent  Number of spaces to add for indentation.
+         *
+         * If this method is not called, the wrapper does not add indentation.
+         */
+        void setIndent(int indent) { indent_ = indent; }
+        /*! \brief
+         * Sets the indentation for first output line after a line break.
+         *
+         * \param[in] indent  Number of spaces to add for indentation.
+         *
+         * If this method is not called, or called with \p indent equal to -1,
+         * the value set with setIndent() is used.
+         */
+        void setFirstLineIndent(int indent) { firstLineIndent_ = indent; }
+        /*! \brief
+         * Sets whether to remove spaces after an explicit newline.
+         *
+         * \param[in] bStrip  If true, spaces after newline are ignored.
+         *
+         * If not removed, the space is added to the indentation set with
+         * setIndent().
+         * The default is to strip such whitespace.
+         */
+        void setStripLeadingWhitespace(bool bStrip)
+        {
+            bStripLeadingWhitespace_ = bStrip;
+        }
+        /*! \brief
+         * Sets a continuation marker for wrapped lines.
+         *
+         * \param[in] continuationChar  Character to use to mark continuation
+         *      lines.
+         *
+         * If set to non-zero character code, this character is added at the
+         * end of each line where a line break is added by TextLineWrapper
+         * (but not after lines produced by explicit line breaks).
+         * The default (\c '\0') is to not add continuation markers.
+         *
+         * Note that currently, the continuation char may cause the output line
+         * length to exceed the value set with setLineLength() by at most two
+         * characters.
+         */
+        void setContinuationChar(char continuationChar)
+        {
+            continuationChar_ = continuationChar;
+        }
+
+        //! Returns the maximum length set with setLineLength().
+        int lineLength() const { return maxLength_; }
+        //! Returns the indentation set with setIndent().
+        int indent() const { return indent_; }
+        /*! \brief
+         * Returns the indentation set with setFirstLineIndent().
+         *
+         * If setFirstLineIndent() has not been called or has been called with
+         * -1, indent() is returned.
+         */
+        int firstLineIndent() const
+        {
+            return (firstLineIndent_ >= 0 ? firstLineIndent_ : indent_);
+        }
+
+    private:
+        //! Maximum length of output lines, or <= 0 if no limit.
+        int                     maxLength_;
+        //! Number of spaces to indent each output line with.
+        int                     indent_;
+        /*! \brief
+         * Number of spaces to indent the first line after a newline.
+         *
+         * If -1, \a indent_ is used.
+         */
+        int                     firstLineIndent_;
+        //! Whether to ignore or preserve space after a newline.
+        bool                    bStripLeadingWhitespace_;
+        //! If not \c '\0', mark each wrapping point with this character.
+        char                    continuationChar_;
+
+        //! Needed to access the members.
+        friend class TextLineWrapper;
+};
+
 /*! \brief
  * Wraps lines to a predefined length.
  *
@@ -188,46 +306,126 @@ std::string replaceAllWords(const std::string &input,
  * longer than a predefined length.  Explicit newlines ('\\n') are preserved.
  * Only space is considered a word separator.  If a single word exceeds the
  * maximum line length, it is still printed on a single line.
- * Extra whitespace is stripped from the start and end of produced lines.
- * If maximum line length is not set using setLineLength(), only wraps at
- * explicit newlines.
- *
- * Two output formats are possible: wrapToString() produces a single string
- * with embedded newlines, and wrapToVector() produces a vector of strings,
- * where each element is one line.
+ * Extra whitespace is stripped from the end of produced lines.
+ * Other options on the wrapping, such as the line length or indentation,
+ * can be changed using a TextLineWrapperSettings object.
+ *
+ * Two interfaces to do the wrapping are provided:
+ *  -# High-level interface using either wrapToString() (produces a single
+ *     string with embedded newlines) or wrapToVector() (produces a vector of
+ *     strings with each line as one element).
+ *     These methods operate on std::string and wrap the entire input string.
+ *  -# Low-level interface using findNextLine() and formatLine().
+ *     findNextLine() operates either on a C string or an std::string, and does
+ *     not do any memory allocation (so it does not throw).  It finds the next
+ *     line to be wrapped, considering the wrapping settings.
+ *     formatLine() does whitespace operations on the line found by
+ *     findNextLine() and returns an std::string.
+ *     These methods allow custom wrapping implementation to either avoid
+ *     exceptions or to wrap only a part of the input string.
  *
  * Typical usage:
  * \code
 gmx::TextLineWrapper wrapper;
-wrapper.setLineLength(78);
+wrapper.settings().setLineLength(78);
 printf("%s\n", wrapper.wrapToString(textToWrap).c_str());
  * \endcode
  *
- * Methods in this class may throw std::bad_alloc if out of memory.
- * Other exceptions are not thrown.
- *
  * \inpublicapi
  * \ingroup module_utility
  */
 class TextLineWrapper
 {
     public:
-        //! Constructs a new line wrapper with no initial wrapping length.
-        TextLineWrapper();
-        ~TextLineWrapper();
+        /*! \brief
+         * Constructs a new line wrapper with default settings.
+         *
+         * Does not throw.
+         */
+        TextLineWrapper()
+        {
+        }
+        /*! \brief
+         * Constructs a new line wrapper with given settings.
+         *
+         * \param[in] settings  Wrapping settings.
+         *
+         * Does not throw.
+         */
+        explicit TextLineWrapper(const TextLineWrapperSettings &settings)
+            : settings_(settings)
+        {
+        }
 
         /*! \brief
-         * Sets the maximum length for output lines.
+         * Provides access to settings of this wrapper.
          *
-         * \param[in] length  Maximum length for the lines after wrapping.
-         * \returns   *this
+         * \returns  The settings object for this wrapper.
          *
-         * If this method is not called, the wrapper has no maximum length
-         * (only wraps at explicit line breaks).
+         * The returned object can be used to modify settings for the wrapper.
+         * All subsequent calls to wrapToString() and wrapToVector() use the
+         * modified settings.
          *
          * Does not throw.
          */
-        TextLineWrapper &setLineLength(int length);
+        TextLineWrapperSettings &settings() { return settings_; }
+
+        /*! \brief
+         * Finds the next line to be wrapped.
+         *
+         * \param[in] input     String to wrap.
+         * \param[in] lineStart Index of first character of the line to find.
+         * \returns   Index of first character of the next line.
+         *
+         * If this is the last line, returns the length of \p input.
+         * In determining the length of the returned line, this function
+         * considers the maximum line length, leaving space for indentation,
+         * and also whitespace stripping behavior.
+         * Thus, the line returned may be longer than the maximum line length
+         * if it has leading and/or trailing space.
+         * When wrapping a line on a space (not on an explicit line break),
+         * the returned index is always on a non-whitespace character after the
+         * space.
+         *
+         * To iterate over lines in a string, use the following code:
+         * \code
+gmx::TextLineWrapper wrapper;
+// <set desired wrapping settings>
+size_t lineStart = 0;
+size_t length = input.length();
+while (lineStart < length)
+{
+    size_t nextLineStart = wrapper.findNextLine(input, lineStart);
+    std::string line = wrapper.formatLine(input, lineStart, nextLineStart));
+    // <do something with the line>
+    lineStart = nextLineStart;
+}
+return result;
+         * \endcode
+         *
+         * Does not throw.
+         */
+        size_t findNextLine(const char *input, size_t lineStart) const;
+        //! \copydoc findNextLine(const char *, size_t) const
+        size_t findNextLine(const std::string &input, size_t lineStart) const;
+        /*! \brief
+         * Formats a single line for output according to wrapping settings.
+         *
+         * \param[in] input     Input string.
+         * \param[in] lineStart Index of first character of the line to format.
+         * \param[in] lineEnd   Index of first character of the next line.
+         * \returns   The line with leading and/or trailing whitespace removed
+         *      and indentation applied.
+         * \throws    std::bad_alloc if out of memory.
+         *
+         * Intended to be used on the lines found by findNextLine().
+         * When used with the lines returned from findNextLine(), the returned
+         * line conforms to the wrapper settings.
+         * Trailing whitespace is always stripped (including any newlines,
+         * i.e., the return value does not contain a newline).
+         */
+        std::string formatLine(const std::string &input,
+                               size_t lineStart, size_t lineEnd) const;
 
         /*! \brief
          * Formats a string, producing a single string with all the lines.
@@ -235,6 +433,7 @@ class TextLineWrapper
          * \param[in] input  String to wrap.
          * \returns   \p input with added newlines such that maximum line
          *      length is not exceeded.
+         * \throws    std::bad_alloc if out of memory.
          *
          * Newlines in the input are preserved, including terminal newlines.
          * Note that if the input does not contain a terminal newline, the
@@ -247,6 +446,7 @@ class TextLineWrapper
          * \param[in] input  String to wrap.
          * \returns   \p input split into lines such that maximum line length
          *      is not exceeded.
+         * \throws    std::bad_alloc if out of memory.
          *
          * The strings in the returned vector do not contain newlines at the
          * end.
@@ -257,9 +457,7 @@ class TextLineWrapper
         std::vector<std::string> wrapToVector(const std::string &input) const;
 
     private:
-        class Impl;
-
-        PrivateImplPointer<Impl> impl_;
+        TextLineWrapperSettings settings_;
 };
 
 } // namespace gmx
diff --git a/src/gromacs/utility/tests/refdata/TextLineWrapperTest_HandlesContinuationCharacter.xml b/src/gromacs/utility/tests/refdata/TextLineWrapperTest_HandlesContinuationCharacter.xml
new file mode 100644 (file)
index 0000000..c726f2f
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="WrappedAt14/12"><![CDATA[
+  A quick brown \
+    fox jumps
+  over the lazy \
+    dog]]></String>
+</ReferenceData>
diff --git a/src/gromacs/utility/tests/refdata/TextLineWrapperTest_HandlesHangingIndent.xml b/src/gromacs/utility/tests/refdata/TextLineWrapperTest_HandlesHangingIndent.xml
new file mode 100644 (file)
index 0000000..793270b
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="WrappedWithNoLimit"><![CDATA[
+  A quick brown fox jumps
+  over the lazy dog]]></String>
+  <String Name="WrappedAt14/12"><![CDATA[
+  A quick brown
+    fox jumps
+  over the lazy
+    dog]]></String>
+</ReferenceData>
diff --git a/src/gromacs/utility/tests/refdata/TextLineWrapperTest_HandlesIndent.xml b/src/gromacs/utility/tests/refdata/TextLineWrapperTest_HandlesIndent.xml
new file mode 100644 (file)
index 0000000..3a117d1
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="WrappedWithNoLimit"><![CDATA[
+  A quick brown fox jumps
+  over the lazy dog]]></String>
+  <String Name="WrappedAt14"><![CDATA[
+  A quick brown
+  fox jumps
+  over the lazy
+  dog]]></String>
+</ReferenceData>
index cfeeb01b4ca52a621dcd9433126404b216d83542..f277fc930b97c4ba9836c38f9b0d203ea8c22603 100644 (file)
@@ -1,9 +1,14 @@
 <?xml version="1.0"?>
 <?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
 <ReferenceData>
-  <String Name="WrappedAt14"><![CDATA[
+  <String Name="WrappedAt14StripLeading"><![CDATA[
 A quick brown
 fox jumps
 over the lazy
+dog]]></String>
+  <String Name="WrappedAt14PreserveLeading"><![CDATA[
+ A quick brown
+fox jumps
+ over the lazy
 dog]]></String>
 </ReferenceData>
index 335f8f0ab66d02369586361805aacde787337542..39c60da1f1e5d2f356d78ca8aeb44fc93d0f0b82 100644 (file)
@@ -174,10 +174,14 @@ TEST(ReplaceAllTest, HandlesPossibleRecursiveMatches)
  * Tests for TextLineWrapper
  */
 
+//! Simple test string for wrapping.
 const char g_wrapText[] = "A quick brown fox jumps over the lazy dog";
+//! Test string for wrapping with embedded line breaks.
 const char g_wrapText2[] = "A quick brown fox jumps\nover the lazy dog";
+//! Test string for wrapping with a long word.
 const char g_wrapTextLongWord[]
     = "A quick brown fox jumps awordthatoverflowsaline over the lazy dog";
+//! Test string for wrapping with extra whitespace.
 const char g_wrapTextWhitespace[] = " A quick brown   fox jumps  \n over the lazy dog";
 
 typedef gmx::test::StringTestBase TextLineWrapperTest;
@@ -234,13 +238,13 @@ TEST_F(TextLineWrapperTest, WrapsCorrectly)
 {
     gmx::TextLineWrapper wrapper;
 
-    wrapper.setLineLength(10);
+    wrapper.settings().setLineLength(10);
     checkText(wrapper.wrapToString(g_wrapText), "WrappedAt10");
     std::vector<std::string> wrapped(wrapper.wrapToVector(g_wrapText));
     checker().checkSequence(wrapped.begin(), wrapped.end(), "WrappedToVector");
-    wrapper.setLineLength(13);
+    wrapper.settings().setLineLength(13);
     checkText(wrapper.wrapToString(g_wrapText), "WrappedAt13");
-    wrapper.setLineLength(14);
+    wrapper.settings().setLineLength(14);
     checkText(wrapper.wrapToString(g_wrapText), "WrappedAt14");
     checkText(wrapper.wrapToString(g_wrapTextLongWord), "WrappedWithLongWord");
 }
@@ -250,18 +254,55 @@ TEST_F(TextLineWrapperTest, WrapsCorrectlyWithExistingBreaks)
     gmx::TextLineWrapper wrapper;
 
     checkText(wrapper.wrapToString(g_wrapText2), "WrappedWithNoLimit");
-    wrapper.setLineLength(10);
+    wrapper.settings().setLineLength(10);
     checkText(wrapper.wrapToString(g_wrapText2), "WrappedAt10");
-    wrapper.setLineLength(14);
+    wrapper.settings().setLineLength(14);
     checkText(wrapper.wrapToString(g_wrapText2), "WrappedAt14");
 }
 
+TEST_F(TextLineWrapperTest, HandlesIndent)
+{
+    gmx::TextLineWrapper wrapper;
+    wrapper.settings().setIndent(2);
+
+    checkText(wrapper.wrapToString(g_wrapText2), "WrappedWithNoLimit");
+    wrapper.settings().setLineLength(16);
+    checkText(wrapper.wrapToString(g_wrapText2), "WrappedAt14");
+}
+
+TEST_F(TextLineWrapperTest, HandlesHangingIndent)
+{
+    gmx::TextLineWrapper wrapper;
+    wrapper.settings().setFirstLineIndent(2);
+    wrapper.settings().setIndent(4);
+
+    checkText(wrapper.wrapToString(g_wrapText2), "WrappedWithNoLimit");
+    wrapper.settings().setLineLength(16);
+    checkText(wrapper.wrapToString(g_wrapText2), "WrappedAt14/12");
+}
+
+TEST_F(TextLineWrapperTest, HandlesContinuationCharacter)
+{
+    gmx::TextLineWrapper wrapper;
+    wrapper.settings().setFirstLineIndent(2);
+    wrapper.settings().setIndent(4);
+    wrapper.settings().setContinuationChar('\\');
+
+    wrapper.settings().setLineLength(16);
+    checkText(wrapper.wrapToString(g_wrapText2), "WrappedAt14/12");
+}
+
 TEST_F(TextLineWrapperTest, WrapsCorrectlyWithExtraWhitespace)
 {
     gmx::TextLineWrapper wrapper;
 
-    wrapper.setLineLength(14);
-    checkText(wrapper.wrapToString(g_wrapTextWhitespace), "WrappedAt14");
+    wrapper.settings().setLineLength(14);
+    wrapper.settings().setStripLeadingWhitespace(true);
+    checkText(wrapper.wrapToString(g_wrapTextWhitespace),
+              "WrappedAt14StripLeading");
+    wrapper.settings().setStripLeadingWhitespace(false);
+    checkText(wrapper.wrapToString(g_wrapTextWhitespace),
+              "WrappedAt14PreserveLeading");
 }
 
 } // namespace