diff --git a/src/catch2/internal/catch_textflow.cpp b/src/catch2/internal/catch_textflow.cpp index 1e39f39a..f8b8e7f4 100644 --- a/src/catch2/internal/catch_textflow.cpp +++ b/src/catch2/internal/catch_textflow.cpp @@ -40,42 +40,50 @@ namespace Catch { namespace TextFlow { void Column::const_iterator::calcLength() { - m_suffix = false; - auto width = m_column.m_width - indent(); - m_end = m_pos; + m_addHyphen = false; + const auto maxLineLength = m_column.m_width - indentSize(); + m_parsedTo = m_lineStart; std::string const& current_line = m_column.m_string; - if ( current_line[m_pos] == '\n' ) { - ++m_end; + if ( current_line[m_lineStart] == '\n' ) { + ++m_parsedTo; } - while ( m_end < current_line.size() && - current_line[m_end] != '\n' ) { - ++m_end; + while ( m_parsedTo < current_line.size() && + current_line[m_parsedTo] != '\n' ) { + ++m_parsedTo; } - if ( m_end < m_pos + width ) { - m_len = m_end - m_pos; + // If we encountered a newline before the column is filled, + // then we linebreak at the newline and consider this line + // finished. + if ( m_parsedTo < m_lineStart + maxLineLength ) { + m_lineLength = m_parsedTo - m_lineStart; } else { - size_t len = width; - while ( len > 0 && !isBoundary( current_line, m_pos + len ) ) { - --len; + // Look for a natural linebreak boundary in the column + // (We look from the end, so that the first found boundary is + // the right one) + size_t newLineLength = maxLineLength; + while ( newLineLength > 0 && !isBoundary( current_line, m_lineStart + newLineLength ) ) { + --newLineLength; } - while ( len > 0 && - isWhitespace( current_line[m_pos + len - 1] ) ) { - --len; + while ( newLineLength > 0 && + isWhitespace( current_line[m_lineStart + newLineLength - 1] ) ) { + --newLineLength; } - if ( len > 0 ) { - m_len = len; + // If we found one, then that is where we linebreak + if ( newLineLength > 0 ) { + m_lineLength = newLineLength; } else { - m_suffix = true; - m_len = width - 1; + // Otherwise we have to split text with a hyphen + m_addHyphen = true; + m_lineLength = maxLineLength - 1; } } } - size_t Column::const_iterator::indent() const { + size_t Column::const_iterator::indentSize() const { auto initial = - m_pos == 0 ? m_column.m_initialIndent : std::string::npos; + m_lineStart == 0 ? m_column.m_initialIndent : std::string::npos; return initial == std::string::npos ? m_column.m_indent : initial; } @@ -83,11 +91,11 @@ namespace Catch { Column::const_iterator::addIndentAndSuffix( size_t position, size_t length ) const { std::string ret; - const auto desired_indent = indent(); - ret.reserve( desired_indent + length + m_suffix ); + const auto desired_indent = indentSize(); + ret.reserve( desired_indent + length + m_addHyphen ); ret.append( desired_indent, ' ' ); ret.append( m_column.m_string, position, length ); - if ( m_suffix ) { + if ( m_addHyphen ) { ret.push_back( '-' ); } @@ -99,29 +107,29 @@ namespace Catch { assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); calcLength(); - if ( m_len == 0 ) { - m_pos = m_column.m_string.size(); + if ( m_lineLength == 0 ) { + m_lineStart = m_column.m_string.size(); } } std::string Column::const_iterator::operator*() const { - assert( m_pos <= m_end ); - return addIndentAndSuffix( m_pos, m_len ); + assert( m_lineStart <= m_parsedTo ); + return addIndentAndSuffix( m_lineStart, m_lineLength ); } Column::const_iterator& Column::const_iterator::operator++() { - m_pos += m_len; + m_lineStart += m_lineLength; std::string const& current_line = m_column.m_string; - if ( m_pos < current_line.size() && current_line[m_pos] == '\n' ) { - m_pos += 1; + if ( m_lineStart < current_line.size() && current_line[m_lineStart] == '\n' ) { + m_lineStart += 1; } else { - while ( m_pos < current_line.size() && - isWhitespace( current_line[m_pos] ) ) { - ++m_pos; + while ( m_lineStart < current_line.size() && + isWhitespace( current_line[m_lineStart] ) ) { + ++m_lineStart; } } - if ( m_pos != current_line.size() ) { + if ( m_lineStart != current_line.size() ) { calcLength(); } return *this; diff --git a/src/catch2/internal/catch_textflow.hpp b/src/catch2/internal/catch_textflow.hpp index 93c6a000..e2cdff58 100644 --- a/src/catch2/internal/catch_textflow.hpp +++ b/src/catch2/internal/catch_textflow.hpp @@ -18,31 +18,49 @@ namespace Catch { class Columns; + /** + * Represents a column of text with specific width and indentation + * + * When written out to a stream, it will perform linebreaking + * of the provided text so that the written lines fit within + * target width. + */ class Column { + // String to be written out std::string m_string; + // Width of the column for linebreaking size_t m_width = CATCH_CONFIG_CONSOLE_WIDTH - 1; + // Indentation of other lines (including first if initial indent is unset) size_t m_indent = 0; + // Indentation of the first line size_t m_initialIndent = std::string::npos; public: + /** + * Iterates "lines" in `Column` and return sthem + */ class const_iterator { friend Column; struct EndTag {}; Column const& m_column; - size_t m_pos = 0; - - size_t m_len = 0; - size_t m_end = 0; - bool m_suffix = false; + // Where does the current line start? + size_t m_lineStart = 0; + // How long should the current line be? + size_t m_lineLength = 0; + // How far have we checked the string to iterate? + size_t m_parsedTo = 0; + // Should a '-' be appended to the line? + bool m_addHyphen = false; const_iterator( Column const& column, EndTag ): - m_column( column ), m_pos( m_column.m_string.size() ) {} + m_column( column ), m_lineStart( m_column.m_string.size() ) {} + // Calculates the length of the current line void calcLength(); // Returns current indention width - size_t indent() const; + size_t indentSize() const; // Creates an indented and (optionally) suffixed string from // current iterator position, indentation and length. @@ -64,7 +82,7 @@ namespace Catch { const_iterator operator++( int ); bool operator==( const_iterator const& other ) const { - return m_pos == other.m_pos && &m_column == &other.m_column; + return m_lineStart == other.m_lineStart && &m_column == &other.m_column; } bool operator!=( const_iterator const& other ) const { return !operator==( other );