diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 7391e4cf3a9ae..e268fb73159f1 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -256,6 +256,17 @@ output format of the diagnostics that it generates. defined and ``-fcolor-diagnostics`` is passed on the command line, Clang will honor the command line argument. + Additionally, if enabled, Clang will semantically highlight the code + the diagnostics pertain to. The ``CLANG_HIGHLIGHT_COLORS`` environment + variable controls this behaviour: + if unset or set to "on", code is highlighted normally. + If set to "off" or "no", highlighting is disabled. + Otherwise, a comma-separated, `token=color` list may be given, + where `token`s may be ``comment``, ``literal``, or ``keyword``, + and `color`s may be ``{bright-,}black,red,green,yellow,blue,magenta,cyan,white``. + If an unknown `color` is given, it's disabled. For example, a valid spec may be + ``CLANG_HIGHLIGHT_COLORS=comment=blue,literal=bright-magenta,keyword=no``. + .. option:: -fansi-escape-codes Controls whether ANSI escape codes are used instead of the Windows Console diff --git a/clang/lib/Frontend/TextDiagnostic.cpp b/clang/lib/Frontend/TextDiagnostic.cpp index 10240d7ee6f2e..0da6fbca27978 100644 --- a/clang/lib/Frontend/TextDiagnostic.cpp +++ b/clang/lib/Frontend/TextDiagnostic.cpp @@ -24,6 +24,7 @@ #include using namespace clang; +using namespace std::literals; static const enum raw_ostream::Colors noteColor = raw_ostream::CYAN; static const enum raw_ostream::Colors remarkColor = @@ -46,9 +47,31 @@ static const enum raw_ostream::Colors savedColor = // is already taken for 'note'. Green is already used to underline // source ranges. White and black are bad because of the usual // terminal backgrounds. Which leaves us only with TWO options. -static constexpr raw_ostream::Colors CommentColor = raw_ostream::YELLOW; -static constexpr raw_ostream::Colors LiteralColor = raw_ostream::GREEN; -static constexpr raw_ostream::Colors KeywordColor = raw_ostream::BLUE; +static std::optional CommentColor = raw_ostream::YELLOW; +static std::optional LiteralColor = raw_ostream::GREEN; +static std::optional KeywordColor = raw_ostream::BLUE; + +static __attribute__((constructor)) void loadHighlightColors() { + auto cfg = std::getenv("CLANG_HIGHLIGHT_COLORS"); + if (!cfg || cfg == "on"sv) + return; + + if (!std::strchr(cfg, '=')) { + CommentColor = LiteralColor = KeywordColor = {}; + return; + } + + const char *const tokens[]{"comment", "literal", "keyword", nullptr}; + std::optional *const colors[]{ + &CommentColor, &LiteralColor, &KeywordColor}; + for (char *value; *cfg;) { + auto idx = getsubopt(&cfg, const_cast(tokens), &value); + if (idx == -1 || !value) + continue; + + *colors[idx] = raw_ostream::parse_color(value); + } +} /// Add highlights to differences in template strings. static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str, @@ -1138,7 +1161,7 @@ highlightLines(StringRef FileData, unsigned StartLineNumber, std::make_unique[]>( EndLineNumber - StartLineNumber + 1); - if (!PP || !ShowColors) + if (!PP || !ShowColors || (!CommentColor && !LiteralColor && !KeywordColor)) return SnippetRanges; // Might cause emission of another diagnostic. @@ -1164,6 +1187,10 @@ highlightLines(StringRef FileData, unsigned StartLineNumber, auto appendStyle = [PP, &LangOpts](SmallVector &Vec, const Token &T, unsigned Start, unsigned Length) -> void { + auto setColor = [&](auto &&color) { + if (color) + Vec.emplace_back(Start, Start + Length, *color); + }; if (T.is(tok::raw_identifier)) { StringRef RawIdent = T.getRawIdentifier(); // Special case true/false/nullptr/... literals, since they will otherwise @@ -1183,18 +1210,18 @@ highlightLines(StringRef FileData, unsigned StartLineNumber, .Case("__FUNCTION__", true) .Case("__FUNCSIG__", true) .Default(false)) { - Vec.emplace_back(Start, Start + Length, LiteralColor); + setColor(LiteralColor); } else { const IdentifierInfo *II = PP->getIdentifierInfo(RawIdent); assert(II); if (II->isKeyword(LangOpts)) - Vec.emplace_back(Start, Start + Length, KeywordColor); + setColor(KeywordColor); } } else if (tok::isLiteral(T.getKind())) { - Vec.emplace_back(Start, Start + Length, LiteralColor); + setColor(LiteralColor); } else { assert(T.is(tok::comment)); - Vec.emplace_back(Start, Start + Length, CommentColor); + setColor(CommentColor); } }; diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h index 696290a5d99cf..c46a00e9304cc 100644 --- a/llvm/include/llvm/Support/raw_ostream.h +++ b/llvm/include/llvm/Support/raw_ostream.h @@ -133,6 +133,9 @@ class raw_ostream { static constexpr Colors SAVEDCOLOR = Colors::SAVEDCOLOR; static constexpr Colors RESET = Colors::RESET; + static std::optional + parse_color(const std::string_view &name) noexcept; + explicit raw_ostream(bool unbuffered = false, OStreamKind K = OStreamKind::OK_OStream) : Kind(K), BufferMode(unbuffered ? BufferKind::Unbuffered diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp index c7064d2dfedc5..db57500255a1a 100644 --- a/llvm/lib/Support/raw_ostream.cpp +++ b/llvm/lib/Support/raw_ostream.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include // may provide O_BINARY. @@ -62,6 +63,7 @@ #endif using namespace llvm; +using namespace std::literals; constexpr raw_ostream::Colors raw_ostream::BLACK; constexpr raw_ostream::Colors raw_ostream::RED; @@ -74,6 +76,44 @@ constexpr raw_ostream::Colors raw_ostream::WHITE; constexpr raw_ostream::Colors raw_ostream::SAVEDCOLOR; constexpr raw_ostream::Colors raw_ostream::RESET; +std::optional +raw_ostream::parse_color(const std::string_view &name) noexcept { + if (name == "black"sv) + return Colors::BLACK; + else if (name == "red"sv) + return Colors::RED; + else if (name == "green"sv) + return Colors::GREEN; + else if (name == "yellow"sv) + return Colors::YELLOW; + else if (name == "blue"sv) + return Colors::BLUE; + else if (name == "magenta"sv) + return Colors::MAGENTA; + else if (name == "cyan"sv) + return Colors::CYAN; + else if (name == "white"sv) + return Colors::WHITE; + else if (name == "bright-black"sv) + return Colors::BRIGHT_BLACK; + else if (name == "bright-red"sv) + return Colors::BRIGHT_RED; + else if (name == "bright-green"sv) + return Colors::BRIGHT_GREEN; + else if (name == "bright-yellow"sv) + return Colors::BRIGHT_YELLOW; + else if (name == "bright-blue"sv) + return Colors::BRIGHT_BLUE; + else if (name == "bright-magenta"sv) + return Colors::BRIGHT_MAGENTA; + else if (name == "bright-cyan"sv) + return Colors::BRIGHT_CYAN; + else if (name == "bright-white"sv) + return Colors::BRIGHT_WHITE; + else + return std::nullopt; +} + raw_ostream::~raw_ostream() { // raw_ostream's subclasses should take care to flush the buffer // in their destructors.