From a3703faa0a04d99ad9f041e20fed48f6c04da69a Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Fri, 5 Apr 2013 07:47:36 +0100 Subject: [PATCH] First cut of new Colour class (to replace TextColour) --- include/internal/catch_console_colour.hpp | 46 +++++++ .../internal/catch_console_colour_impl.hpp | 109 +++++++++++++++- include/internal/catch_list.hpp | 6 +- include/reporters/catch_reporter_basic.hpp | 2 +- projects/SelfTest/TestMain.cpp | 118 ++++++++++++++++++ .../CatchSelfTest.xcodeproj/project.pbxproj | 4 +- 6 files changed, 275 insertions(+), 10 deletions(-) diff --git a/include/internal/catch_console_colour.hpp b/include/internal/catch_console_colour.hpp index 930738dc..4811a683 100644 --- a/include/internal/catch_console_colour.hpp +++ b/include/internal/catch_console_colour.hpp @@ -12,6 +12,52 @@ namespace Catch { + namespace Detail { + struct IColourImpl; + } + + struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + + // By intention + FileName = Grey, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = Yellow, + + SecondaryText = Grey, + Headers = White + }; + + Colour( Code _colourCode ); + ~Colour(); + static void use( Code _colourCode ); + + private: + static Detail::IColourImpl* impl; + }; + struct IConsoleColourCodes : NonCopyable { enum Colours { None, diff --git a/include/internal/catch_console_colour_impl.hpp b/include/internal/catch_console_colour_impl.hpp index d00d969e..6c5ca588 100644 --- a/include/internal/catch_console_colour_impl.hpp +++ b/include/internal/catch_console_colour_impl.hpp @@ -10,13 +10,60 @@ #include "catch_console_colour.hpp" +namespace Catch { namespace Detail { + struct IColourImpl { + virtual ~IColourImpl() {} + virtual void use( Colour::Code _colourCode ) = 0; + }; +}} + #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// #include +namespace Catch { namespace { - using namespace Catch; + class Win32ColourImpl : public Detail::IColourImpl { + public: + Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) + { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); + originalAttributes = csbiInfo.wAttributes; + } + ~Win32ColourImpl() { + use( Colour::None ); + } + + virtual void use( Colour::Code _colourCode ) { + switch( _colourCode ) { + case Colour::None: return setTextAttribute( originalAttributes ); + case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::Red: return setTextAttribute( FOREGROUND_RED ); + case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); + case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); + case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); + case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::Grey: return setTextAttribute( 0 ); + + case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); + case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); + case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); + case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + + case Colour::Bright: throw std::logic_error( "not a colour" ); + } + } + + private: + void setTextAttribute( WORD _textAttribute ) { + SetConsoleTextAttribute( stdoutHandle, _textAttribute ); + } + HANDLE stdoutHandle; + WORD originalAttributes; + }; + WORD mapConsoleColour( IConsoleColourCodes::Colours colour ) { enum Win32Colours { Grey = FOREGROUND_INTENSITY, @@ -71,15 +118,48 @@ namespace { } typedef WindowsConsoleColourCodes PlatformConsoleColourCodes; + Win32ColourImpl platformColourImpl; } // end anon namespace +} // end namespace Catch #else // Not Windows - assumed to be POSIX compatible ////////////////////////// #include +namespace Catch { namespace { - using namespace Catch; + + class PosixColourImpl : public Detail::IColourImpl { + public: + PosixColourImpl() { + use( Colour::None ); + } + + virtual void use( Colour::Code _colourCode ) { + switch( _colourCode ) { + case Colour::None: + case Colour::White: return setColour( "[0m" ); + case Colour::Red: return setColour( "[0;31m" ); + case Colour::Green: return setColour( "[0;32m" ); + case Colour::Blue: return setColour( "[0:34m" ); + case Colour::Cyan: return setColour( "[0;36m" ); + case Colour::Yellow: return setColour( "[0;33m" ); + case Colour::Grey: return setColour( "[1;30m" ); + + case Colour::LightGrey: return setColour( "[0;37m" ); + case Colour::BrightRed: return setColour( "[1;31m" ); + case Colour::BrightGreen: return setColour( "[1;33m" ); + case Colour::BrightWhite: return setColour( "[1;37m" ); + + case Colour::Bright: throw std::logic_error( "not a colour" ); + } + } + private: + void setColour( const char* _escapeCode ) { + std::cout << '\033' << _escapeCode; + } + }; // use POSIX/ ANSI console terminal codes // Implementation contributed by Adam Strzelecki (http://github.com/nanoant) @@ -126,19 +206,40 @@ namespace { } typedef AnsiConsoleColourCodes PlatformConsoleColourCodes; + PosixColourImpl platformColourImpl; -} // namespace Catch +} // end anon namespace +} // end namespace Catch #endif // not Windows namespace { - struct NoConsoleColourCodes : IConsoleColourCodes { + struct NoConsoleColourCodes : Catch::IConsoleColourCodes { void set( Colours ) {} }; } namespace Catch { + namespace { + struct NoColourImpl : Detail::IColourImpl { + void use( Colour::Code ) {} + }; + NoColourImpl noColourImpl; + static const bool shouldUseColour = shouldUseColourForPlatform() && + !isDebuggerActive(); + } + + Colour::Colour( Code _colourCode ){ use( _colourCode ); } + Colour::~Colour(){ use( None ); } + void Colour::use( Code _colourCode ) { + impl->use( _colourCode ); + } + + Detail::IColourImpl* Colour::impl = shouldUseColour + ? static_cast( &platformColourImpl ) + : static_cast( &noColourImpl ); + TextColour::TextColour( Colours colour ) : m_impl( NULL ) { static bool s_shouldUseColour = shouldUseColourForPlatform() && !isDebuggerActive(); diff --git a/include/internal/catch_list.hpp b/include/internal/catch_list.hpp index 762447f3..61fd64f9 100644 --- a/include/internal/catch_list.hpp +++ b/include/internal/catch_list.hpp @@ -118,7 +118,7 @@ namespace Catch { tagItEnd = it->getTestCaseInfo().tags.end(); tagIt != tagItEnd; ++tagIt ) { - std::string tagName = "[" + *tagIt + "]"; + std::string tagName = *tagIt; maxTagLen = (std::max)( maxTagLen, tagName.size() ); std::map::iterator countIt = tagCounts.find( tagName ); if( countIt == tagCounts.end() ) @@ -128,7 +128,7 @@ namespace Catch { } } } - maxTagLen +=2; + maxTagLen +=4; if( maxTagLen > CATCH_CONFIG_CONSOLE_WIDTH-10 ) maxTagLen = CATCH_CONFIG_CONSOLE_WIDTH-10; @@ -136,7 +136,7 @@ namespace Catch { countIt != countItEnd; ++countIt ) { LineWrapper wrapper; - wrapper.setIndent(2).setRight( maxTagLen ).wrap( countIt->first ); + wrapper.setIndent(2).setRight( maxTagLen ).wrap( "[" + countIt->first + "]" ); std::cout << wrapper; std::size_t dots = 2; diff --git a/include/reporters/catch_reporter_basic.hpp b/include/reporters/catch_reporter_basic.hpp index b13f84f4..15c79908 100644 --- a/include/reporters/catch_reporter_basic.hpp +++ b/include/reporters/catch_reporter_basic.hpp @@ -64,7 +64,7 @@ namespace Catch { m_config.stream() << "No tests ran"; } else if( totals.assertions.failed ) { - TextColour colour( TextColour::ResultError ); + Colour colour( Colour::ResultError ); ReportCounts( "test case", totals.testCases, allPrefix ); if( totals.testCases.failed > 0 ) { m_config.stream() << " ("; diff --git a/projects/SelfTest/TestMain.cpp b/projects/SelfTest/TestMain.cpp index 18b9c727..579caae5 100644 --- a/projects/SelfTest/TestMain.cpp +++ b/projects/SelfTest/TestMain.cpp @@ -495,3 +495,121 @@ TEST_CASE( "Long strings can be wrapped", "[wrap]" ) { } } + +using namespace Catch; + +class ColourString { +public: + + struct ColourIndex { + ColourIndex( Colour::Code _colour, std::size_t _fromIndex, std::size_t _toIndex ) + : colour( _colour ), + fromIndex( _fromIndex ), + toIndex( _toIndex ) + {} + + Colour::Code colour; + std::size_t fromIndex; + std::size_t toIndex; + }; + + ColourString( std::string const& _string ) + : string( _string ) + {} + ColourString( std::string const& _string, std::vector const& _colours ) + : string( _string ), colours( _colours ) + {} + + ColourString& addColour( Colour::Code colour, int _index ) { + colours.push_back( ColourIndex( colour, + resolveRelativeIndex( _index ), + resolveRelativeIndex( _index )+1 ) ); + return *this; + } + ColourString& addColour( Colour::Code colour, int _fromIndex, int _toIndex ) { + colours.push_back( ColourIndex( colour, + resolveRelativeIndex(_fromIndex), + resolveLastRelativeIndex( _toIndex ) ) ); + return *this; + } + + void writeToStream( std::ostream& _stream ) const { + std::size_t last = 0; + for( std::size_t i = 0; i < colours.size(); ++i ) { + ColourIndex const& index = colours[i]; + if( index.fromIndex > last ) + _stream << string.substr( last, index.fromIndex-last ); + { + Colour colourGuard( index.colour ); + _stream << string.substr( index.fromIndex, index.toIndex-index.fromIndex ); + } + last = index.toIndex; + } + if( last < string.size() ) + _stream << string.substr( last ); + } + friend std::ostream& operator << ( std::ostream& _stream, ColourString const& _colourString ) { + _colourString.writeToStream( _stream ); + return _stream; + } + +private: + + std::size_t resolveLastRelativeIndex( int _index ) { + std::size_t index = resolveRelativeIndex( _index ); + return index == 0 ? string.size() : index; + } + std::size_t resolveRelativeIndex( int _index ) { + return static_cast( _index >= 0 + ? _index + : static_cast( string.size() )+_index ); + } + std::string string; + std::vector colours; +}; + +class Text +{ +public: + Text( std::string const& _string ) : originalString( _string ) { + } + Text( char const* const _string ) : originalString( _string ) { + } + + friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + _text.print( _stream ); + return _stream; + } + +private: + void process() const { + + } + void print( std::ostream& stream ) const { + stream << originalString; + } + + std::string originalString; +// std::vector< +}; + +TEST_CASE( "Strings can be rendered with colour", "[colour]" ) { +// Text text = "`red`This is in red. `green` this is in green"; +// std::cout << text << std::endl; + + { + ColourString cs( "hello" ); + cs .addColour( Colour::Red, 0 ) + .addColour( Colour::Green, -1 ); + + std::cout << cs << std::endl; + } + + { + ColourString cs( "hello" ); + cs .addColour( Colour::Blue, 1, -2 ); + + std::cout << cs << std::endl; + } + +} diff --git a/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj b/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj index 114da60b..8f799bb5 100644 --- a/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj +++ b/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj @@ -138,8 +138,8 @@ 4A9D84B315599AC900FBB209 /* catch_expressionresult_builder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = catch_expressionresult_builder.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 4AA7B8B4165428BA003155F6 /* catch_version.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = catch_version.hpp; path = ../../../../include/internal/catch_version.hpp; sourceTree = ""; }; 4AA7FF4115F3E89D009AD7F9 /* BDDTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BDDTests.cpp; sourceTree = ""; }; - 4AB1C73514F97BDA00F31DF7 /* catch_console_colour_impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_console_colour_impl.hpp; sourceTree = ""; }; - 4AB1C73714F97C1300F31DF7 /* catch_console_colour.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_console_colour.hpp; sourceTree = ""; }; + 4AB1C73514F97BDA00F31DF7 /* catch_console_colour_impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = catch_console_colour_impl.hpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 4AB1C73714F97C1300F31DF7 /* catch_console_colour.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = catch_console_colour.hpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; 4AB3D99C1616216500C9A0F8 /* catch_interfaces_testcase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_interfaces_testcase.cpp; path = ../../../SelfTest/SurrogateCpps/catch_interfaces_testcase.cpp; sourceTree = ""; }; 4AB3D99F1616219100C9A0F8 /* catch_interfaces_config.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_interfaces_config.cpp; path = ../../../SelfTest/SurrogateCpps/catch_interfaces_config.cpp; sourceTree = ""; }; 4AB3D9A1161621B500C9A0F8 /* catch_interfaces_generators.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_interfaces_generators.cpp; path = ../../../SelfTest/SurrogateCpps/catch_interfaces_generators.cpp; sourceTree = ""; };