mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-26 07:16:10 +01:00
Split TextFlow out from Clara
Now that it has its own header, various reporter TUs that want to format text do not have to also include Clara. Together with outlining implementations from a header into a separate TU, this has noticeably improved the compilation times of the testing impl. As part of this split, I also implemented some improvements to the TextFlow code in comparison to the upstream code. These are: * Replaced the `Spacer` type with a free function that constructs special `Column` that does the same thing. * Generic performance improvements, such as eliminating needless allocations, reserving space in needed allocations, and using smarter algorithms in some places. * Because `Column` only ever stored 1 string in its vector, it now holds the string directly instead.
This commit is contained in:
parent
81aa2d5582
commit
e7aa432850
@ -119,7 +119,7 @@ set(INTERNAL_HEADERS
|
||||
${SOURCES_DIR}/internal/catch_test_registry.hpp
|
||||
${SOURCES_DIR}/catch_test_spec.hpp
|
||||
${SOURCES_DIR}/internal/catch_test_spec_parser.hpp
|
||||
${SOURCES_DIR}/internal/catch_text.hpp
|
||||
${SOURCES_DIR}/internal/catch_textflow.hpp
|
||||
${SOURCES_DIR}/catch_timer.hpp
|
||||
${SOURCES_DIR}/internal/catch_to_string.hpp
|
||||
${SOURCES_DIR}/catch_tostring.hpp
|
||||
@ -175,6 +175,7 @@ set(IMPL_SOURCES
|
||||
${SOURCES_DIR}/internal/catch_test_case_registry_impl.cpp
|
||||
${SOURCES_DIR}/internal/catch_test_case_tracker.cpp
|
||||
${SOURCES_DIR}/internal/catch_test_registry.cpp
|
||||
${SOURCES_DIR}/internal/catch_textflow.cpp
|
||||
${SOURCES_DIR}/catch_test_spec.cpp
|
||||
${SOURCES_DIR}/internal/catch_test_spec_parser.cpp
|
||||
${SOURCES_DIR}/catch_timer.cpp
|
||||
|
@ -83,7 +83,7 @@
|
||||
#include <catch2/internal/catch_test_macro_impl.hpp>
|
||||
#include <catch2/internal/catch_test_registry.hpp>
|
||||
#include <catch2/internal/catch_test_spec_parser.hpp>
|
||||
#include <catch2/internal/catch_text.hpp>
|
||||
#include <catch2/internal/catch_textflow.hpp>
|
||||
#include <catch2/internal/catch_to_string.hpp>
|
||||
#include <catch2/internal/catch_uncaught_exceptions.hpp>
|
||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
|
||||
#include <catch2/internal/catch_random_number_generator.hpp>
|
||||
#include <catch2/internal/catch_startup_exception_registry.hpp>
|
||||
#include <catch2/internal/catch_text.hpp>
|
||||
#include <catch2/internal/catch_textflow.hpp>
|
||||
#include <catch2/internal/catch_windows_h_proxy.hpp>
|
||||
#include <catch2/reporters/catch_reporter_listening.hpp>
|
||||
|
||||
@ -144,7 +144,7 @@ namespace Catch {
|
||||
try {
|
||||
std::rethrow_exception(ex_ptr);
|
||||
} catch ( std::exception const& ex ) {
|
||||
Catch::cerr() << Column( ex.what() ).indent(2) << '\n';
|
||||
Catch::cerr() << TextFlow::Column( ex.what() ).indent(2) << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -182,7 +182,7 @@ namespace Catch {
|
||||
Catch::cerr()
|
||||
<< Colour( Colour::Red )
|
||||
<< "\nError(s) in input:\n"
|
||||
<< Column( result.errorMessage() ).indent( 2 )
|
||||
<< TextFlow::Column( result.errorMessage() ).indent( 2 )
|
||||
<< "\n\n";
|
||||
Catch::cerr() << "Run with -? for usage\n" << std::endl;
|
||||
return MaxExitCode;
|
||||
|
@ -9,11 +9,13 @@
|
||||
#include <catch2/interfaces/catch_interfaces_config.hpp>
|
||||
#include <catch2/catch_config.hpp>
|
||||
#include <catch2/internal/catch_console_colour.hpp>
|
||||
#include <catch2/internal/catch_console_width.hpp>
|
||||
#include <catch2/catch_message.hpp>
|
||||
#include <catch2/internal/catch_list.hpp>
|
||||
#include <catch2/internal/catch_text.hpp>
|
||||
#include <catch2/internal/catch_textflow.hpp>
|
||||
#include <catch2/internal/catch_string_manip.hpp>
|
||||
#include <catch2/catch_test_case_info.hpp>
|
||||
#include <catch2/internal/catch_textflow.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
@ -117,15 +119,15 @@ namespace Catch {
|
||||
for (auto const& desc : descriptions) {
|
||||
if (config.verbosity() == Verbosity::Quiet) {
|
||||
Catch::cout()
|
||||
<< Column(desc.name)
|
||||
<< TextFlow::Column(desc.name)
|
||||
.indent(2)
|
||||
.width(5 + maxNameLen) << '\n';
|
||||
} else {
|
||||
Catch::cout()
|
||||
<< Column(desc.name + ":")
|
||||
<< TextFlow::Column(desc.name + ":")
|
||||
.indent(2)
|
||||
.width(5 + maxNameLen)
|
||||
+ Column(desc.description)
|
||||
+ TextFlow::Column(desc.description)
|
||||
.initialIndent(0)
|
||||
.indent(2)
|
||||
.width(CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8)
|
||||
@ -149,12 +151,12 @@ namespace Catch {
|
||||
: Colour::None;
|
||||
Colour colourGuard(colour);
|
||||
|
||||
Catch::cout() << Column(testCaseInfo.name).initialIndent(2).indent(4) << '\n';
|
||||
Catch::cout() << TextFlow::Column(testCaseInfo.name).initialIndent(2).indent(4) << '\n';
|
||||
if (config.verbosity() >= Verbosity::High) {
|
||||
Catch::cout() << Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << std::endl;
|
||||
Catch::cout() << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << std::endl;
|
||||
}
|
||||
if (!testCaseInfo.tags.empty() && config.verbosity() > Verbosity::Quiet) {
|
||||
Catch::cout() << Column(testCaseInfo.tagsAsString()).indent(6) << '\n';
|
||||
Catch::cout() << TextFlow::Column(testCaseInfo.tagsAsString()).indent(6) << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,7 +178,7 @@ namespace Catch {
|
||||
ReusableStringStream rss;
|
||||
rss << " " << std::setw(2) << tagCount.count << " ";
|
||||
auto str = rss.str();
|
||||
auto wrapper = Column(tagCount.all())
|
||||
auto wrapper = TextFlow::Column(tagCount.all())
|
||||
.initialIndent(0)
|
||||
.indent(str.size())
|
||||
.width(CATCH_CONFIG_CONSOLE_WIDTH - 10);
|
||||
|
@ -22,7 +22,6 @@
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wweak-vtables"
|
||||
#pragma clang diagnostic ignored "-Wexit-time-destructors"
|
||||
#pragma clang diagnostic ignored "-Wshadow"
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
|
@ -14,10 +14,6 @@
|
||||
#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80
|
||||
#endif
|
||||
|
||||
#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
|
||||
#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH
|
||||
#endif
|
||||
|
||||
#ifndef CLARA_CONFIG_OPTIONAL_TYPE
|
||||
#ifdef __has_include
|
||||
#if __has_include(<optional>) && __cplusplus >= 201703L
|
||||
@ -28,356 +24,16 @@
|
||||
#endif
|
||||
|
||||
|
||||
// ----------- #included from clara_textflow.hpp -----------
|
||||
#include <catch2/internal/catch_textflow.hpp>
|
||||
|
||||
// TextFlowCpp
|
||||
//
|
||||
// A single-header library for wrapping and laying out basic text, by Phil Nash
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// This project is hosted at https://github.com/philsquared/textflowcpp
|
||||
|
||||
#ifndef CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
|
||||
#define CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
|
||||
#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80
|
||||
#endif
|
||||
|
||||
|
||||
namespace Catch {
|
||||
namespace clara {
|
||||
namespace TextFlow {
|
||||
|
||||
inline auto isWhitespace(char c) -> bool {
|
||||
static std::string chars = " \t\n\r";
|
||||
return chars.find(c) != std::string::npos;
|
||||
}
|
||||
inline auto isBreakableBefore(char c) -> bool {
|
||||
static std::string chars = "[({<|";
|
||||
return chars.find(c) != std::string::npos;
|
||||
}
|
||||
inline auto isBreakableAfter(char c) -> bool {
|
||||
static std::string chars = "])}>.,:;*+-=&/\\";
|
||||
return chars.find(c) != std::string::npos;
|
||||
}
|
||||
|
||||
class Columns;
|
||||
|
||||
class Column {
|
||||
std::vector<std::string> m_strings;
|
||||
size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH;
|
||||
size_t m_indent = 0;
|
||||
size_t m_initialIndent = std::string::npos;
|
||||
|
||||
public:
|
||||
class iterator {
|
||||
friend Column;
|
||||
|
||||
Column const& m_column;
|
||||
size_t m_stringIndex = 0;
|
||||
size_t m_pos = 0;
|
||||
|
||||
size_t m_len = 0;
|
||||
size_t m_end = 0;
|
||||
bool m_suffix = false;
|
||||
|
||||
iterator(Column const& column, size_t stringIndex)
|
||||
: m_column(column),
|
||||
m_stringIndex(stringIndex) {}
|
||||
|
||||
auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; }
|
||||
|
||||
auto isBoundary(size_t at) const -> bool {
|
||||
assert(at > 0);
|
||||
assert(at <= line().size());
|
||||
|
||||
return at == line().size() ||
|
||||
(isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) ||
|
||||
isBreakableBefore(line()[at]) ||
|
||||
isBreakableAfter(line()[at - 1]);
|
||||
}
|
||||
|
||||
void calcLength() {
|
||||
assert(m_stringIndex < m_column.m_strings.size());
|
||||
|
||||
m_suffix = false;
|
||||
auto width = m_column.m_width - indent();
|
||||
m_end = m_pos;
|
||||
if (line()[m_pos] == '\n') {
|
||||
++m_end;
|
||||
}
|
||||
while (m_end < line().size() && line()[m_end] != '\n')
|
||||
++m_end;
|
||||
|
||||
if (m_end < m_pos + width) {
|
||||
m_len = m_end - m_pos;
|
||||
} else {
|
||||
size_t len = width;
|
||||
while (len > 0 && !isBoundary(m_pos + len))
|
||||
--len;
|
||||
while (len > 0 && isWhitespace(line()[m_pos + len - 1]))
|
||||
--len;
|
||||
|
||||
if (len > 0) {
|
||||
m_len = len;
|
||||
} else {
|
||||
m_suffix = true;
|
||||
m_len = width - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto indent() const -> size_t {
|
||||
auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos;
|
||||
return initial == std::string::npos ? m_column.m_indent : initial;
|
||||
}
|
||||
|
||||
auto addIndentAndSuffix(std::string const &plain) const -> std::string {
|
||||
return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain);
|
||||
}
|
||||
|
||||
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(Column const& column) : m_column(column) {
|
||||
assert(m_column.m_width > m_column.m_indent);
|
||||
assert(m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent);
|
||||
calcLength();
|
||||
if (m_len == 0)
|
||||
m_stringIndex++; // Empty string
|
||||
}
|
||||
|
||||
auto operator *() const -> std::string {
|
||||
assert(m_stringIndex < m_column.m_strings.size());
|
||||
assert(m_pos <= m_end);
|
||||
return addIndentAndSuffix(line().substr(m_pos, m_len));
|
||||
}
|
||||
|
||||
auto operator ++() -> iterator& {
|
||||
m_pos += m_len;
|
||||
if (m_pos < line().size() && line()[m_pos] == '\n')
|
||||
m_pos += 1;
|
||||
else
|
||||
while (m_pos < line().size() && isWhitespace(line()[m_pos]))
|
||||
++m_pos;
|
||||
|
||||
if (m_pos == line().size()) {
|
||||
m_pos = 0;
|
||||
++m_stringIndex;
|
||||
}
|
||||
if (m_stringIndex < m_column.m_strings.size())
|
||||
calcLength();
|
||||
return *this;
|
||||
}
|
||||
auto operator ++(int) -> iterator {
|
||||
iterator prev(*this);
|
||||
operator++();
|
||||
return prev;
|
||||
}
|
||||
|
||||
auto operator ==(iterator const& other) const -> bool {
|
||||
return
|
||||
m_pos == other.m_pos &&
|
||||
m_stringIndex == other.m_stringIndex &&
|
||||
&m_column == &other.m_column;
|
||||
}
|
||||
auto operator !=(iterator const& other) const -> bool {
|
||||
return !operator==(other);
|
||||
}
|
||||
};
|
||||
using const_iterator = iterator;
|
||||
|
||||
explicit Column(std::string const& text) { m_strings.push_back(text); }
|
||||
|
||||
auto width(size_t newWidth) -> Column& {
|
||||
assert(newWidth > 0);
|
||||
m_width = newWidth;
|
||||
return *this;
|
||||
}
|
||||
auto indent(size_t newIndent) -> Column& {
|
||||
m_indent = newIndent;
|
||||
return *this;
|
||||
}
|
||||
auto initialIndent(size_t newIndent) -> Column& {
|
||||
m_initialIndent = newIndent;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto width() const -> size_t { return m_width; }
|
||||
auto begin() const -> iterator { return iterator(*this); }
|
||||
auto end() const -> iterator { return { *this, m_strings.size() }; }
|
||||
|
||||
inline friend std::ostream& operator << (std::ostream& os, Column const& col) {
|
||||
bool first = true;
|
||||
for (auto line : col) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
os << "\n";
|
||||
os << line;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
auto operator + (Column const& other)->Columns;
|
||||
|
||||
auto toString() const -> std::string {
|
||||
std::ostringstream oss;
|
||||
oss << *this;
|
||||
return oss.str();
|
||||
}
|
||||
};
|
||||
|
||||
class Spacer : public Column {
|
||||
|
||||
public:
|
||||
explicit Spacer(size_t spaceWidth) : Column("") {
|
||||
width(spaceWidth);
|
||||
}
|
||||
};
|
||||
|
||||
class Columns {
|
||||
std::vector<Column> m_columns;
|
||||
|
||||
public:
|
||||
|
||||
class iterator {
|
||||
friend Columns;
|
||||
struct EndTag {};
|
||||
|
||||
std::vector<Column> const& m_columns;
|
||||
std::vector<Column::iterator> m_iterators;
|
||||
size_t m_activeIterators;
|
||||
|
||||
iterator(Columns const& columns, EndTag)
|
||||
: m_columns(columns.m_columns),
|
||||
m_activeIterators(0) {
|
||||
m_iterators.reserve(m_columns.size());
|
||||
|
||||
for (auto const& col : m_columns)
|
||||
m_iterators.push_back(col.end());
|
||||
}
|
||||
|
||||
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)
|
||||
: m_columns(columns.m_columns),
|
||||
m_activeIterators(m_columns.size()) {
|
||||
m_iterators.reserve(m_columns.size());
|
||||
|
||||
for (auto const& col : m_columns)
|
||||
m_iterators.push_back(col.begin());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
auto operator *() const -> std::string {
|
||||
std::string row, padding;
|
||||
|
||||
for (size_t i = 0; i < m_columns.size(); ++i) {
|
||||
auto width = m_columns[i].width();
|
||||
if (m_iterators[i] != m_columns[i].end()) {
|
||||
std::string col = *m_iterators[i];
|
||||
row += padding + col;
|
||||
if (col.size() < width)
|
||||
padding = std::string(width - col.size(), ' ');
|
||||
else
|
||||
padding = "";
|
||||
} else {
|
||||
padding += std::string(width, ' ');
|
||||
}
|
||||
}
|
||||
return row;
|
||||
}
|
||||
auto operator ++() -> iterator& {
|
||||
for (size_t i = 0; i < m_columns.size(); ++i) {
|
||||
if (m_iterators[i] != m_columns[i].end())
|
||||
++m_iterators[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
auto operator ++(int) -> iterator {
|
||||
iterator prev(*this);
|
||||
operator++();
|
||||
return prev;
|
||||
}
|
||||
};
|
||||
using const_iterator = iterator;
|
||||
|
||||
auto begin() const -> iterator { return iterator(*this); }
|
||||
auto end() const -> iterator { return { *this, iterator::EndTag() }; }
|
||||
|
||||
auto operator += (Column const& col) -> Columns& {
|
||||
m_columns.push_back(col);
|
||||
return *this;
|
||||
}
|
||||
auto operator + (Column const& col) -> Columns {
|
||||
Columns combined = *this;
|
||||
combined += col;
|
||||
return combined;
|
||||
}
|
||||
|
||||
inline friend std::ostream& operator << (std::ostream& os, Columns const& cols) {
|
||||
|
||||
bool first = true;
|
||||
for (auto line : cols) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
os << "\n";
|
||||
os << line;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
auto toString() const -> std::string {
|
||||
std::ostringstream oss;
|
||||
oss << *this;
|
||||
return oss.str();
|
||||
}
|
||||
};
|
||||
|
||||
inline auto Column::operator + (Column const& other) -> Columns {
|
||||
Columns cols;
|
||||
cols += *this;
|
||||
cols += other;
|
||||
return cols;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif // CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
|
||||
|
||||
// ----------- end of #include from clara_textflow.hpp -----------
|
||||
// ........... back in clara.hpp
|
||||
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) )
|
||||
#define CATCH_PLATFORM_WINDOWS
|
||||
@ -989,7 +645,7 @@ namespace detail {
|
||||
oss << opt;
|
||||
}
|
||||
if( !m_hint.empty() )
|
||||
oss << " <" << m_hint << ">";
|
||||
oss << " <" << m_hint << '>';
|
||||
return { { oss.str(), m_description } };
|
||||
}
|
||||
|
||||
@ -1122,26 +778,26 @@ namespace detail {
|
||||
|
||||
void writeToStream( std::ostream &os ) const {
|
||||
if (!m_exeName.name().empty()) {
|
||||
os << "usage:\n" << " " << m_exeName.name() << " ";
|
||||
os << "usage:\n" << " " << m_exeName.name() << ' ';
|
||||
bool required = true, first = true;
|
||||
for( auto const &arg : m_args ) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
os << " ";
|
||||
os << ' ';
|
||||
if( arg.isOptional() && required ) {
|
||||
os << "[";
|
||||
os << '[';
|
||||
required = false;
|
||||
}
|
||||
os << "<" << arg.hint() << ">";
|
||||
os << '<' << arg.hint() << '>';
|
||||
if( arg.cardinality() == 0 )
|
||||
os << " ... ";
|
||||
}
|
||||
if( !required )
|
||||
os << "]";
|
||||
os << ']';
|
||||
if( !m_options.empty() )
|
||||
os << " options";
|
||||
os << "\n\nwhere options are:" << std::endl;
|
||||
os << "\n\nwhere options are:\n";
|
||||
}
|
||||
|
||||
auto rows = getHelpColumns();
|
||||
@ -1157,7 +813,7 @@ namespace detail {
|
||||
TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) +
|
||||
TextFlow::Spacer(4) +
|
||||
TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth );
|
||||
os << row << std::endl;
|
||||
os << row << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Created by Phil on 10/2/2014.
|
||||
* Copyright 2014 Two Blue Cubes Ltd. All rights reserved.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
#ifndef TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_clara.hpp>
|
||||
|
||||
namespace Catch {
|
||||
using namespace clara::TextFlow;
|
||||
}
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
|
235
src/catch2/internal/catch_textflow.cpp
Normal file
235
src/catch2/internal/catch_textflow.cpp
Normal file
@ -0,0 +1,235 @@
|
||||
#include <catch2/internal/catch_textflow.hpp>
|
||||
#include <cstring>
|
||||
#include <ostream>
|
||||
|
||||
namespace {
|
||||
bool isWhitespace( char c ) {
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
|
||||
}
|
||||
|
||||
bool isBreakableBefore( char c ) {
|
||||
static const char chars[] = "[({<|";
|
||||
return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr;
|
||||
}
|
||||
|
||||
bool isBreakableAfter( char c ) {
|
||||
static const char chars[] = "])}>.,:;*+-=&/\\";
|
||||
return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr;
|
||||
}
|
||||
|
||||
bool isBoundary( std::string const& line, size_t at ) {
|
||||
assert( at > 0 );
|
||||
assert( at <= line.size() );
|
||||
|
||||
return at == line.size() ||
|
||||
( isWhitespace( line[at] ) && !isWhitespace( line[at - 1] ) ) ||
|
||||
isBreakableBefore( line[at] ) ||
|
||||
isBreakableAfter( line[at - 1] );
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Catch {
|
||||
namespace TextFlow {
|
||||
|
||||
void Column::iterator::calcLength() {
|
||||
m_suffix = false;
|
||||
auto width = m_column.m_width - indent();
|
||||
m_end = m_pos;
|
||||
std::string const& current_line = m_column.m_string;
|
||||
if ( current_line[m_pos] == '\n' ) {
|
||||
++m_end;
|
||||
}
|
||||
while ( m_end < current_line.size() &&
|
||||
current_line[m_end] != '\n' ) {
|
||||
++m_end;
|
||||
}
|
||||
|
||||
if ( m_end < m_pos + width ) {
|
||||
m_len = m_end - m_pos;
|
||||
} else {
|
||||
size_t len = width;
|
||||
while ( len > 0 && !isBoundary( current_line, m_pos + len ) ) {
|
||||
--len;
|
||||
}
|
||||
while ( len > 0 &&
|
||||
isWhitespace( current_line[m_pos + len - 1] ) ) {
|
||||
--len;
|
||||
}
|
||||
|
||||
if ( len > 0 ) {
|
||||
m_len = len;
|
||||
} else {
|
||||
m_suffix = true;
|
||||
m_len = width - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t Column::iterator::indent() const {
|
||||
auto initial =
|
||||
m_pos == 0 ? m_column.m_initialIndent : std::string::npos;
|
||||
return initial == std::string::npos ? m_column.m_indent : initial;
|
||||
}
|
||||
|
||||
std::string
|
||||
Column::iterator::addIndentAndSuffix( size_t position,
|
||||
size_t length ) const {
|
||||
std::string ret;
|
||||
const auto desired_indent = indent();
|
||||
ret.reserve( desired_indent + length + m_suffix );
|
||||
ret.append( desired_indent, ' ' );
|
||||
ret.append( m_column.m_string, position, length );
|
||||
if ( m_suffix ) {
|
||||
ret.push_back( '-' );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Column::iterator::iterator( Column const& column ): m_column( column ) {
|
||||
assert( m_column.m_width > m_column.m_indent );
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
std::string Column::iterator::operator*() const {
|
||||
assert( m_pos <= m_end );
|
||||
return addIndentAndSuffix( m_pos, m_len );
|
||||
}
|
||||
|
||||
Column::iterator& Column::iterator::operator++() {
|
||||
m_pos += m_len;
|
||||
std::string const& current_line = m_column.m_string;
|
||||
if ( m_pos < current_line.size() && current_line[m_pos] == '\n' ) {
|
||||
m_pos += 1;
|
||||
} else {
|
||||
while ( m_pos < current_line.size() &&
|
||||
isWhitespace( current_line[m_pos] ) ) {
|
||||
++m_pos;
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_pos != current_line.size() ) {
|
||||
calcLength();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Column::iterator Column::iterator::operator++( int ) {
|
||||
iterator prev( *this );
|
||||
operator++();
|
||||
return prev;
|
||||
}
|
||||
|
||||
std::ostream& operator<<( std::ostream& os, Column const& col ) {
|
||||
bool first = true;
|
||||
for ( auto line : col ) {
|
||||
if ( first ) {
|
||||
first = false;
|
||||
} else {
|
||||
os << '\n';
|
||||
}
|
||||
os << line;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
Column Spacer( size_t spaceWidth ) {
|
||||
Column ret{ "" };
|
||||
ret.width( spaceWidth );
|
||||
return ret;
|
||||
}
|
||||
|
||||
Columns::iterator::iterator( Columns const& columns, EndTag ):
|
||||
m_columns( columns.m_columns ), m_activeIterators( 0 ) {
|
||||
|
||||
m_iterators.reserve( m_columns.size() );
|
||||
for ( auto const& col : m_columns ) {
|
||||
m_iterators.push_back( col.end() );
|
||||
}
|
||||
}
|
||||
|
||||
Columns::iterator::iterator( Columns const& columns ):
|
||||
m_columns( columns.m_columns ),
|
||||
m_activeIterators( m_columns.size() ) {
|
||||
|
||||
m_iterators.reserve( m_columns.size() );
|
||||
for ( auto const& col : m_columns ) {
|
||||
m_iterators.push_back( col.begin() );
|
||||
}
|
||||
}
|
||||
|
||||
std::string Columns::iterator::operator*() const {
|
||||
std::string row, padding;
|
||||
|
||||
for ( size_t i = 0; i < m_columns.size(); ++i ) {
|
||||
const auto width = m_columns[i].width();
|
||||
if ( m_iterators[i] != m_columns[i].end() ) {
|
||||
std::string col = *m_iterators[i];
|
||||
row += padding;
|
||||
row += col;
|
||||
|
||||
padding.clear();
|
||||
if ( col.size() < width ) {
|
||||
padding.append( width - col.size(), ' ' );
|
||||
}
|
||||
} else {
|
||||
padding.append( width, ' ' );
|
||||
}
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
Columns::iterator& Columns::iterator::operator++() {
|
||||
for ( size_t i = 0; i < m_columns.size(); ++i ) {
|
||||
if ( m_iterators[i] != m_columns[i].end() ) {
|
||||
++m_iterators[i];
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Columns::iterator Columns::iterator::operator++( int ) {
|
||||
iterator prev( *this );
|
||||
operator++();
|
||||
return prev;
|
||||
}
|
||||
|
||||
std::ostream& operator<<( std::ostream& os, Columns const& cols ) {
|
||||
bool first = true;
|
||||
for ( auto line : cols ) {
|
||||
if ( first ) {
|
||||
first = false;
|
||||
} else {
|
||||
os << '\n';
|
||||
}
|
||||
os << line;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
Columns Column::operator+( Column const& other ) {
|
||||
Columns cols;
|
||||
cols += *this;
|
||||
cols += other;
|
||||
return cols;
|
||||
}
|
||||
|
||||
Columns& Columns::operator+=( Column const& col ) {
|
||||
m_columns.push_back( col );
|
||||
return *this;
|
||||
}
|
||||
|
||||
Columns Columns::operator+( Column const& col ) {
|
||||
Columns combined = *this;
|
||||
combined += col;
|
||||
return combined;
|
||||
}
|
||||
|
||||
} // namespace TextFlow
|
||||
} // namespace Catch
|
144
src/catch2/internal/catch_textflow.hpp
Normal file
144
src/catch2/internal/catch_textflow.hpp
Normal file
@ -0,0 +1,144 @@
|
||||
#ifndef CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
|
||||
#define CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <catch2/internal/catch_console_width.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Catch {
|
||||
namespace TextFlow {
|
||||
|
||||
class Columns;
|
||||
|
||||
class Column {
|
||||
std::string m_string;
|
||||
size_t m_width = CATCH_CONFIG_CONSOLE_WIDTH - 1;
|
||||
size_t m_indent = 0;
|
||||
size_t m_initialIndent = std::string::npos;
|
||||
|
||||
public:
|
||||
class 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;
|
||||
|
||||
iterator( Column const& column, EndTag ):
|
||||
m_column( column ), m_pos( m_column.m_string.size() ) {}
|
||||
|
||||
void calcLength();
|
||||
|
||||
// Returns current indention width
|
||||
size_t indent() const;
|
||||
|
||||
// Creates an indented and (optionally) suffixed string from
|
||||
// current iterator position, indentation and length.
|
||||
std::string addIndentAndSuffix( size_t position,
|
||||
size_t length ) 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 iterator( Column const& column );
|
||||
|
||||
std::string operator*() const;
|
||||
|
||||
iterator& operator++();
|
||||
iterator operator++( int );
|
||||
|
||||
bool operator==( iterator const& other ) const {
|
||||
return m_pos == other.m_pos && &m_column == &other.m_column;
|
||||
}
|
||||
bool operator!=( iterator const& other ) const {
|
||||
return !operator==( other );
|
||||
}
|
||||
};
|
||||
using const_iterator = iterator;
|
||||
|
||||
explicit Column( std::string const& text ): m_string( text ) {}
|
||||
|
||||
Column& width( size_t newWidth ) {
|
||||
assert( newWidth > 0 );
|
||||
m_width = newWidth;
|
||||
return *this;
|
||||
}
|
||||
Column& indent( size_t newIndent ) {
|
||||
m_indent = newIndent;
|
||||
return *this;
|
||||
}
|
||||
Column& initialIndent( size_t newIndent ) {
|
||||
m_initialIndent = newIndent;
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t width() const { return m_width; }
|
||||
iterator begin() const { return iterator( *this ); }
|
||||
iterator end() const { return { *this, iterator::EndTag{} }; }
|
||||
|
||||
friend std::ostream& operator<<( std::ostream& os,
|
||||
Column const& col );
|
||||
|
||||
Columns operator+( Column const& other );
|
||||
};
|
||||
|
||||
//! Creates a column that serves as an empty space of specific width
|
||||
Column Spacer( size_t spaceWidth );
|
||||
|
||||
class Columns {
|
||||
std::vector<Column> m_columns;
|
||||
|
||||
public:
|
||||
class iterator {
|
||||
friend Columns;
|
||||
struct EndTag {};
|
||||
|
||||
std::vector<Column> const& m_columns;
|
||||
std::vector<Column::iterator> 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() }; }
|
||||
|
||||
Columns& operator+=( Column const& col );
|
||||
Columns operator+( Column const& col );
|
||||
|
||||
friend std::ostream& operator<<( std::ostream& os,
|
||||
Columns const& cols );
|
||||
};
|
||||
|
||||
} // namespace TextFlow
|
||||
} // namespace Catch
|
||||
#endif // CATCH_CLARA_TEXTFLOW_HPP_INCLUDED
|
@ -14,12 +14,11 @@
|
||||
#include <catch2/internal/catch_console_colour.hpp>
|
||||
#include <catch2/internal/catch_string_manip.hpp>
|
||||
#include <catch2/catch_version.hpp>
|
||||
#include <catch2/internal/catch_text.hpp>
|
||||
#include <catch2/internal/catch_textflow.hpp>
|
||||
#include <catch2/internal/catch_stream.hpp>
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
#include <catch2/catch_test_case_info.hpp>
|
||||
#include <catch2/internal/catch_console_width.hpp>
|
||||
#include <catch2/internal/catch_stream.hpp>
|
||||
|
||||
#include <cfloat>
|
||||
#include <cstdio>
|
||||
@ -152,7 +151,7 @@ private:
|
||||
if (result.hasExpandedExpression()) {
|
||||
stream << "with expansion:\n";
|
||||
Colour colourGuard(Colour::ReconstructedExpression);
|
||||
stream << Column(result.getExpandedExpression()).indent(2) << '\n';
|
||||
stream << TextFlow::Column(result.getExpandedExpression()).indent(2) << '\n';
|
||||
}
|
||||
}
|
||||
void printMessage() const {
|
||||
@ -161,7 +160,7 @@ private:
|
||||
for (auto const& msg : messages) {
|
||||
// If this assertion is a warning ignore any INFO messages
|
||||
if (printInfoMessages || msg.type != ResultWas::Info)
|
||||
stream << Column(msg.message).indent(2) << '\n';
|
||||
stream << TextFlow::Column(msg.message).indent(2) << '\n';
|
||||
}
|
||||
}
|
||||
void printSourceInfo() const {
|
||||
@ -297,10 +296,10 @@ public:
|
||||
m_isOpen = true;
|
||||
*this << RowBreak();
|
||||
|
||||
Columns headerCols;
|
||||
Spacer spacer(2);
|
||||
TextFlow::Columns headerCols;
|
||||
auto spacer = TextFlow::Spacer(2);
|
||||
for (auto const& info : m_columnInfos) {
|
||||
headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2));
|
||||
headerCols += TextFlow::Column(info.name).width(static_cast<std::size_t>(info.width - 2));
|
||||
headerCols += spacer;
|
||||
}
|
||||
m_os << headerCols << '\n';
|
||||
@ -438,7 +437,7 @@ void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
|
||||
void ConsoleReporter::benchmarkPreparing(std::string const& name) {
|
||||
lazyPrintWithoutClosingBenchmarkTable();
|
||||
|
||||
auto nameCol = Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2));
|
||||
auto nameCol = TextFlow::Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2));
|
||||
|
||||
bool firstLine = true;
|
||||
for (auto line : nameCol) {
|
||||
@ -585,7 +584,7 @@ void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t
|
||||
i += 2;
|
||||
else
|
||||
i = 0;
|
||||
stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n';
|
||||
stream << TextFlow::Column(_string).indent(indent + i).initialIndent(indent) << '\n';
|
||||
}
|
||||
|
||||
struct SummaryColumn {
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include <catch2/catch_tostring.hpp>
|
||||
#include <catch2/internal/catch_string_manip.hpp>
|
||||
#include <catch2/catch_reporter_registrars.hpp>
|
||||
#include <catch2/internal/catch_text.hpp>
|
||||
#include <catch2/internal/catch_textflow.hpp>
|
||||
#include <catch2/interfaces/catch_interfaces_config.hpp>
|
||||
#include <catch2/catch_test_case_info.hpp>
|
||||
|
||||
@ -266,7 +266,7 @@ namespace Catch {
|
||||
}
|
||||
if (result.hasExpandedExpression()) {
|
||||
rss << "with expansion:\n";
|
||||
rss << Column(result.getExpandedExpression()).indent(2) << '\n';
|
||||
rss << TextFlow::Column(result.getExpandedExpression()).indent(2) << '\n';
|
||||
}
|
||||
} else {
|
||||
rss << '\n';
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
#include <catch2/internal/catch_string_manip.hpp>
|
||||
#include <catch2/internal/catch_enforce.hpp>
|
||||
#include <catch2/internal/catch_text.hpp>
|
||||
#include <catch2/internal/catch_textflow.hpp>
|
||||
#include <catch2/catch_test_case_info.hpp>
|
||||
|
||||
#include <cassert>
|
||||
@ -23,7 +23,7 @@ namespace Catch {
|
||||
i += 2;
|
||||
else
|
||||
i = 0;
|
||||
os << Column(_string)
|
||||
os << TextFlow::Column(_string)
|
||||
.indent(indent + i)
|
||||
.initialIndent(indent) << '\n';
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user