Index: llvm/include/llvm/Support/WithColor.h =================================================================== --- llvm/include/llvm/Support/WithColor.h +++ llvm/include/llvm/Support/WithColor.h @@ -11,6 +11,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/CommandLine.h" +#include namespace llvm { @@ -32,31 +33,107 @@ Remark }; +/// A color settings saved in WithColorContext. +struct ColorState { + raw_ostream::Colors Color; + bool Bold; + bool BG; + + ColorState(raw_ostream::Colors Color, bool Bold, bool BG) + : Color(Color), Bold(Bold), BG(BG) {} +}; + +/// A context for NestableWithColor which holds saved colors. +class WithColorContext { + std::stack Colors; + +public: + void pushColor(raw_ostream::Colors Color, bool Bold, bool BG = false) { + Colors.emplace(Color, Bold, BG); + } + + void popColor() { Colors.pop(); } + bool empty() { return Colors.empty(); } + const ColorState ¤tColor() { return Colors.top(); } +}; + /// An RAII object that temporarily switches an output stream to a specific /// color. class WithColor { raw_ostream &OS; bool DisableColors; + WithColorContext *Context; public: - /// To be used like this: WithColor(OS, HighlightColor::String) << "text"; + /// To change colors temporarily, use WithColor like this: + /// + /// WithColor(OS, HighlightColor::String) << "text"; + /// + /// If WithColorContext is given, it restores colors properly: + /// + /// WithColorContext Ctx; + /// { + /// WithColor AddrColor(OS, HighlightColor::Address, false, &Ctx); + /// AddrColor << "AAA"; + /// { + /// WithColor StringColor(OS, HighlightColor::String, false, &Ctx); + /// StringColor << "BBB"; + /// // The Blue's destructor is called here and it restores the + /// // AddrColor's color state. + /// } + /// AddrColor << "CCC\n"; + /// // The AddrColor's destructor resets the colors to defaults. + /// } + /// + /// Otherwise, WithColor resets colors in the destructor regardless of the + /// previous color. In the example above, "CCC" will be printed in the + /// terminal default colors, not in HighlightColor::String colors. + /// /// @param OS The output stream /// @param S Symbolic name for syntax element to color /// @param DisableColors Whether to ignore color changes regardless of -color /// and support in OS - WithColor(raw_ostream &OS, HighlightColor S, bool DisableColors = false); - /// To be used like this: WithColor(OS, raw_ostream::Black) << "text"; + /// @param Context A WithColorContext object. Set nullptr if you don't need + /// to restore colors. + WithColor(raw_ostream &OS, HighlightColor S, bool DisableColors = false, + WithColorContext *Context = nullptr); + /// To change colors temporarily, use WithColor like this: + /// + /// WithColor(OS, raw_ostream::Black) << "text"; + /// + /// If WithColorContext is given, it restores colors properly: + /// + /// WithColorContext Ctx; + /// { + /// WithColor Red(OS, raw_ostream::RED, false, false, false, &Ctx); + /// Red << "AAA"; + /// { + /// WithColor Blue(OS, raw_ostream::BLUE, false, false, false, &Ctx); + /// Blue << "BBB"; + /// // The Blue's destructor is called here and it restores the Red's + /// // color state. + /// } + /// Red << "CCC\n"; + /// // The Red's destructor resets the colors to defaults. + /// } + /// + /// Otherwise, WithColor resets colors in the destructor regardless of the + /// previous color. In the example above, "CCC" will be printed in the + /// terminal default color, not in red. + /// /// @param OS The output stream /// @param Color ANSI color to use, the special SAVEDCOLOR can be used to /// change only the bold attribute, and keep colors untouched /// @param Bold Bold/brighter text, default false /// @param BG If true, change the background, default: change foreground /// @param DisableColors Whether to ignore color changes regardless of -color - /// and support in OS + /// @param Context A WithColorContext object. Set nullptr if you don't need + /// to restore colors. WithColor(raw_ostream &OS, raw_ostream::Colors Color = raw_ostream::SAVEDCOLOR, - bool Bold = false, bool BG = false, bool DisableColors = false) - : OS(OS), DisableColors(DisableColors) { + bool Bold = false, bool BG = false, bool DisableColors = false, + WithColorContext *Context = nullptr) + : OS(OS), DisableColors(DisableColors), Context(Context) { changeColor(Color, Bold, BG); } ~WithColor(); @@ -83,16 +160,20 @@ /// Convenience method for printing "error: " to the given stream. static raw_ostream &error(raw_ostream &OS, StringRef Prefix = "", - bool DisableColors = false); + bool DisableColors = false, + WithColorContext *Context = nullptr); /// Convenience method for printing "warning: " to the given stream. static raw_ostream &warning(raw_ostream &OS, StringRef Prefix = "", - bool DisableColors = false); + bool DisableColors = false, + WithColorContext *Context = nullptr); /// Convenience method for printing "note: " to the given stream. static raw_ostream ¬e(raw_ostream &OS, StringRef Prefix = "", - bool DisableColors = false); + bool DisableColors = false, + WithColorContext *Context = nullptr); /// Convenience method for printing "remark: " to the given stream. static raw_ostream &remark(raw_ostream &OS, StringRef Prefix = "", - bool DisableColors = false); + bool DisableColors = false, + WithColorContext *Context = nullptr); /// Determine whether colors are displayed. bool colorsEnabled(); Index: llvm/lib/Support/WithColor.cpp =================================================================== --- llvm/lib/Support/WithColor.cpp +++ llvm/lib/Support/WithColor.cpp @@ -18,40 +18,41 @@ cl::desc("Use colors in output (default=autodetect)"), cl::init(cl::BOU_UNSET)); -WithColor::WithColor(raw_ostream &OS, HighlightColor Color, bool DisableColors) - : OS(OS), DisableColors(DisableColors) { +WithColor::WithColor(raw_ostream &OS, HighlightColor Color, bool DisableColors, + WithColorContext *Context) + : OS(OS), DisableColors(DisableColors), Context(Context) { // Detect color from terminal type unless the user passed the --color option. if (colorsEnabled()) { switch (Color) { case HighlightColor::Address: - OS.changeColor(raw_ostream::YELLOW); + changeColor(raw_ostream::YELLOW); break; case HighlightColor::String: - OS.changeColor(raw_ostream::GREEN); + changeColor(raw_ostream::GREEN); break; case HighlightColor::Tag: - OS.changeColor(raw_ostream::BLUE); + changeColor(raw_ostream::BLUE); break; case HighlightColor::Attribute: - OS.changeColor(raw_ostream::CYAN); + changeColor(raw_ostream::CYAN); break; case HighlightColor::Enumerator: - OS.changeColor(raw_ostream::MAGENTA); + changeColor(raw_ostream::MAGENTA); break; case HighlightColor::Macro: - OS.changeColor(raw_ostream::RED); + changeColor(raw_ostream::RED); break; case HighlightColor::Error: - OS.changeColor(raw_ostream::RED, true); + changeColor(raw_ostream::RED, true); break; case HighlightColor::Warning: - OS.changeColor(raw_ostream::MAGENTA, true); + changeColor(raw_ostream::MAGENTA, true); break; case HighlightColor::Note: - OS.changeColor(raw_ostream::BLACK, true); + changeColor(raw_ostream::BLACK, true); break; case HighlightColor::Remark: - OS.changeColor(raw_ostream::BLUE, true); + changeColor(raw_ostream::BLUE, true); break; } } @@ -66,33 +67,34 @@ raw_ostream &WithColor::remark() { return remark(errs()); } raw_ostream &WithColor::error(raw_ostream &OS, StringRef Prefix, - bool DisableColors) { + bool DisableColors, WithColorContext *Context) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Error, DisableColors).get() + return WithColor(OS, HighlightColor::Error, DisableColors, Context).get() << "error: "; } raw_ostream &WithColor::warning(raw_ostream &OS, StringRef Prefix, - bool DisableColors) { + bool DisableColors, WithColorContext *Context) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Warning, DisableColors).get() + return WithColor(OS, HighlightColor::Warning, DisableColors, Context).get() << "warning: "; } raw_ostream &WithColor::note(raw_ostream &OS, StringRef Prefix, - bool DisableColors) { + bool DisableColors, WithColorContext *Context) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Note, DisableColors).get() << "note: "; + return WithColor(OS, HighlightColor::Note, DisableColors, Context).get() + << "note: "; } raw_ostream &WithColor::remark(raw_ostream &OS, StringRef Prefix, - bool DisableColors) { + bool DisableColors, WithColorContext *Context) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Remark, DisableColors).get() + return WithColor(OS, HighlightColor::Remark, DisableColors, Context).get() << "remark: "; } @@ -106,8 +108,13 @@ WithColor &WithColor::changeColor(raw_ostream::Colors Color, bool Bold, bool BG) { - if (colorsEnabled()) + if (colorsEnabled()) { + if (Context) + Context->pushColor(Color, Bold, BG); + OS.changeColor(Color, Bold, BG); + } + return *this; } @@ -117,4 +124,15 @@ return *this; } -WithColor::~WithColor() { resetColor(); } +WithColor::~WithColor() { + if (colorsEnabled()) { + OS.resetColor(); + if (Context) { + Context->popColor(); + if (!Context->empty()) { + const ColorState &State = Context->currentColor(); + OS.changeColor(State.Color, State.Bold, State.BG); + } + } + } +}