[clang] [llvm] [clang][Diagnostics] Add CLANG_HIGHLIGHT_COLORS= to configure syntax highlighting in diagnostics (#82945) (PR #83060)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 26 13:45:09 PST 2024
https://github.com/nabijaczleweli updated https://github.com/llvm/llvm-project/pull/83060
>From a786f5c27ce7d25553a224bcc92185755b3875d7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= <nabijaczleweli at nabijaczleweli.xyz>
Date: Mon, 26 Feb 2024 21:58:24 +0100
Subject: [PATCH] [clang][Diagnostics] Add CLANG_HIGHLIGHT_COLORS= to configure
syntax highlighting in diagnostics (#82945)
CLANG_HIGHLIGHT_COLORS=on is equivalent to unset,
and enables the default.
CLANG_HIGHLIGHT_COLORS=off (or =no, or anything to that effect)
disables highlighting altogether.
CLANG_HIGHLIGHT_COLORS=comment=red highlights comments in red,
and the rest as-default.
CLANG_HIGHLIGHT_COLORS=comment=blue,literal=bright-magenta,keyword=no
highlights comments in blue, literals in bright magenta,
and doesn't highlight keywords.
Fixes: 718aac9f7a19227b5c5ec85819a3a5ae24ce32e1
---
clang/docs/UsersManual.rst | 11 +++++++
clang/lib/Frontend/TextDiagnostic.cpp | 43 ++++++++++++++++++++-----
llvm/include/llvm/Support/raw_ostream.h | 3 ++
llvm/lib/Support/raw_ostream.cpp | 40 +++++++++++++++++++++++
4 files changed, 89 insertions(+), 8 deletions(-)
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 7391e4cf3a9aeb..e268fb73159f12 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 10240d7ee6f2e6..0da6fbca27978b 100644
--- a/clang/lib/Frontend/TextDiagnostic.cpp
+++ b/clang/lib/Frontend/TextDiagnostic.cpp
@@ -24,6 +24,7 @@
#include <optional>
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<raw_ostream::Colors> CommentColor = raw_ostream::YELLOW;
+static std::optional<raw_ostream::Colors> LiteralColor = raw_ostream::GREEN;
+static std::optional<raw_ostream::Colors> 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<raw_ostream::Colors> *const colors[]{
+ &CommentColor, &LiteralColor, &KeywordColor};
+ for (char *value; *cfg;) {
+ auto idx = getsubopt(&cfg, const_cast<char *const *>(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<SmallVector<TextDiagnostic::StyleRange>[]>(
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<TextDiagnostic::StyleRange> &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 696290a5d99cf5..c46a00e9304ccd 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<Colors>
+ 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 c7064d2dfedc56..db57500255a1a8 100644
--- a/llvm/lib/Support/raw_ostream.cpp
+++ b/llvm/lib/Support/raw_ostream.cpp
@@ -27,6 +27,7 @@
#include <algorithm>
#include <cerrno>
#include <cstdio>
+#include <string_view>
#include <sys/stat.h>
// <fcntl.h> 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::Colors>
+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.
More information about the cfe-commits
mailing list