From bc524d70253a4ab2fe40c3ca3e5666e267c0a4d1 Mon Sep 17 00:00:00 2001 From: Lexi Winter Date: Sun, 29 Jun 2025 19:25:29 +0100 Subject: import catch2 3.8.1 --- src/catch2/internal/catch_textflow.hpp | 298 +++++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 src/catch2/internal/catch_textflow.hpp (limited to 'src/catch2/internal/catch_textflow.hpp') diff --git a/src/catch2/internal/catch_textflow.hpp b/src/catch2/internal/catch_textflow.hpp new file mode 100644 index 0000000..2d9d78a --- /dev/null +++ b/src/catch2/internal/catch_textflow.hpp @@ -0,0 +1,298 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +#ifndef CATCH_TEXTFLOW_HPP_INCLUDED +#define CATCH_TEXTFLOW_HPP_INCLUDED + +#include +#include + +#include +#include +#include + +namespace Catch { + namespace TextFlow { + + class Columns; + + /** + * Abstraction for a string with ansi escape sequences that + * automatically skips over escapes when iterating. Only graphical + * escape sequences are considered. + * + * Internal representation: + * An escape sequence looks like \033[39;49m + * We need bidirectional iteration and the unbound length of escape + * sequences poses a problem for operator-- To make this work we'll + * replace the last `m` with a 0xff (this is a codepoint that won't have + * any utf-8 meaning). + */ + class AnsiSkippingString { + std::string m_string; + std::size_t m_size = 0; + + // perform 0xff replacement and calculate m_size + void preprocessString(); + + public: + class const_iterator; + using iterator = const_iterator; + // note: must be u-suffixed or this will cause a "truncation of + // constant value" warning on MSVC + static constexpr char sentinel = static_cast( 0xffu ); + + explicit AnsiSkippingString( std::string const& text ); + explicit AnsiSkippingString( std::string&& text ); + + const_iterator begin() const; + const_iterator end() const; + + size_t size() const { return m_size; } + + std::string substring( const_iterator begin, + const_iterator end ) const; + }; + + class AnsiSkippingString::const_iterator { + friend AnsiSkippingString; + struct EndTag {}; + + const std::string* m_string; + std::string::const_iterator m_it; + + explicit const_iterator( const std::string& string, EndTag ): + m_string( &string ), m_it( string.end() ) {} + + void tryParseAnsiEscapes(); + void advance(); + void unadvance(); + + public: + using difference_type = std::ptrdiff_t; + using value_type = char; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::bidirectional_iterator_tag; + + explicit const_iterator( const std::string& string ): + m_string( &string ), m_it( string.begin() ) { + tryParseAnsiEscapes(); + } + + char operator*() const { return *m_it; } + + const_iterator& operator++() { + advance(); + return *this; + } + const_iterator operator++( int ) { + iterator prev( *this ); + operator++(); + return prev; + } + const_iterator& operator--() { + unadvance(); + return *this; + } + const_iterator operator--( int ) { + iterator prev( *this ); + operator--(); + return prev; + } + + bool operator==( const_iterator const& other ) const { + return m_it == other.m_it; + } + bool operator!=( const_iterator const& other ) const { + return !operator==( other ); + } + bool operator<=( const_iterator const& other ) const { + return m_it <= other.m_it; + } + + const_iterator oneBefore() const { + auto it = *this; + return --it; + } + }; + + /** + * 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 + AnsiSkippingString 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 returns them + */ + class const_iterator { + friend Column; + struct EndTag {}; + + Column const& m_column; + // Where does the current line start? + AnsiSkippingString::const_iterator m_lineStart; + // How long should the current line be? + AnsiSkippingString::const_iterator m_lineEnd; + // How far have we checked the string to iterate? + AnsiSkippingString::const_iterator m_parsedTo; + // Should a '-' be appended to the line? + bool m_addHyphen = false; + + const_iterator( Column const& column, EndTag ): + m_column( column ), + m_lineStart( m_column.m_string.end() ), + m_lineEnd( column.m_string.end() ), + m_parsedTo( column.m_string.end() ) {} + + // Calculates the length of the current line + void calcLength(); + + // Returns current indentation width + size_t indentSize() const; + + // Creates an indented and (optionally) suffixed string from + // current iterator position, indentation and length. + std::string addIndentAndSuffix( + AnsiSkippingString::const_iterator start, + AnsiSkippingString::const_iterator end ) const; + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::forward_iterator_tag; + + explicit const_iterator( Column const& column ); + + std::string operator*() const; + + const_iterator& operator++(); + const_iterator operator++( int ); + + bool operator==( const_iterator const& other ) const { + return m_lineStart == other.m_lineStart && + &m_column == &other.m_column; + } + bool operator!=( const_iterator const& other ) const { + return !operator==( other ); + } + }; + using iterator = const_iterator; + + explicit Column( std::string const& text ): m_string( text ) {} + explicit Column( std::string&& text ): + m_string( CATCH_MOVE( text ) ) {} + + Column& width( size_t newWidth ) & { + assert( newWidth > 0 ); + m_width = newWidth; + return *this; + } + Column&& width( size_t newWidth ) && { + assert( newWidth > 0 ); + m_width = newWidth; + return CATCH_MOVE( *this ); + } + Column& indent( size_t newIndent ) & { + m_indent = newIndent; + return *this; + } + Column&& indent( size_t newIndent ) && { + m_indent = newIndent; + return CATCH_MOVE( *this ); + } + Column& initialIndent( size_t newIndent ) & { + m_initialIndent = newIndent; + return *this; + } + Column&& initialIndent( size_t newIndent ) && { + m_initialIndent = newIndent; + return CATCH_MOVE( *this ); + } + + size_t width() const { return m_width; } + const_iterator begin() const { return const_iterator( *this ); } + const_iterator end() const { + return { *this, const_iterator::EndTag{} }; + } + + friend std::ostream& operator<<( std::ostream& os, + Column const& col ); + + friend Columns operator+( Column const& lhs, Column const& rhs ); + friend Columns operator+( Column&& lhs, Column&& rhs ); + }; + + //! Creates a column that serves as an empty space of specific width + Column Spacer( size_t spaceWidth ); + + class Columns { + std::vector m_columns; + + public: + class iterator { + friend Columns; + struct EndTag {}; + + std::vector const& m_columns; + std::vector m_iterators; + size_t m_activeIterators; + + iterator( Columns const& columns, EndTag ); + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::forward_iterator_tag; + + explicit iterator( Columns const& columns ); + + auto operator==( iterator const& other ) const -> bool { + return m_iterators == other.m_iterators; + } + auto operator!=( iterator const& other ) const -> bool { + return m_iterators != other.m_iterators; + } + std::string operator*() const; + iterator& operator++(); + iterator operator++( int ); + }; + using const_iterator = iterator; + + iterator begin() const { return iterator( *this ); } + iterator end() const { return { *this, iterator::EndTag() }; } + + friend Columns& operator+=( Columns& lhs, Column const& rhs ); + friend Columns& operator+=( Columns& lhs, Column&& rhs ); + friend Columns operator+( Columns const& lhs, Column const& rhs ); + friend Columns operator+( Columns&& lhs, Column&& rhs ); + + friend std::ostream& operator<<( std::ostream& os, + Columns const& cols ); + }; + + } // namespace TextFlow +} // namespace Catch +#endif // CATCH_TEXTFLOW_HPP_INCLUDED -- cgit v1.2.3