When available, use ANSI escapes to colorize diagnostic output.

Issue #245.
This commit is contained in:
Matt Pharr
2012-04-19 11:36:28 -07:00
parent cc26b66e99
commit e4b3d03da5
2 changed files with 108 additions and 14 deletions

View File

@@ -17,6 +17,10 @@ import shlex
import platform
import tempfile
# disable fancy error/warning printing with ANSI colors, so grepping for error
# messages doesn't get confused
os.environ["TERM"] = "dumb"
# This script is affected by http://bugs.python.org/issue5261 on OSX 10.5 Leopard
# git history has a workaround for that issue.

118
util.cpp
View File

@@ -90,6 +90,49 @@ lTerminalWidth() {
}
static bool
lHaveANSIColors() {
static bool r = (getenv("TERM") != NULL &&
strcmp(getenv("TERM"), "dumb") != 0);
return r;
}
static const char *
lStartBold() {
if (lHaveANSIColors())
return "\e[1m";
else
return "";
}
static const char *
lStartRed() {
if (lHaveANSIColors())
return "\e[31m";
else
return "";
}
static const char *
lStartBlue() {
if (lHaveANSIColors())
return "\e[34m";
else
return "";
}
static const char *
lResetColor() {
if (lHaveANSIColors())
return "\e[0m";
else
return "";
}
/** Given a pointer into a string, find the end of the current word and
return a pointer to its last character.
*/
@@ -140,17 +183,43 @@ lPrintFileLineContext(SourcePos p) {
fclose(f);
}
/** Counts the number of characters into the buf at which the numColons
colon character is found. Skips over ANSI escape sequences and doesn't
include their characters in the final count.
*/
static int
lFindIndent(int numColons, const char *buf) {
int indent = 0;
while (*buf != '\0') {
if (*buf == '\e') {
while (*buf != '\0' && *buf != 'm')
++buf;
if (*buf == 'm')
++buf;
}
else {
if (*buf == ':') {
if (--numColons == 0)
break;
}
++indent;
++buf;
}
}
return indent + 2;
}
/** Print the given string to the given FILE, assuming the given output
column width. Break words as needed to avoid words spilling past the
last column. */
static void
lPrintWithWordBreaks(const char *buf, int columnWidth, FILE *out) {
lPrintWithWordBreaks(const char *buf, int indent, int columnWidth, FILE *out) {
#ifdef ISPC_IS_WINDOWS
fputs(buf, out);
#else
int column = 0;
Assert(strchr(buf, ':') != NULL);
int indent = strchr(buf, ':') - buf + 2;
int width = std::max(40, columnWidth - 2);
// Collect everything into a string and print it all at once at the end
@@ -160,6 +229,15 @@ lPrintWithWordBreaks(const char *buf, int columnWidth, FILE *out) {
const char *msgPos = buf;
while (true) {
if (*msgPos == '\e') {
// handle ANSI color escape: copy it to the output buffer
// without charging for the characters it uses
do {
outStr.push_back(*msgPos++);
} while (*msgPos != '\0' && *msgPos != 'm');
continue;
}
while (*msgPos != '\0' && isspace(*msgPos))
++msgPos;
if (*msgPos == '\0')
@@ -171,8 +249,8 @@ lPrintWithWordBreaks(const char *buf, int columnWidth, FILE *out) {
column = indent;
outStr.push_back('\n');
// Indent to the same column as the ":" at the start of the
// message, unless doing so would be too far in.
for (int i = 0; i < std::min(16, indent); ++i)
// message.
for (int i = 0; i < indent; ++i)
outStr.push_back(' ');
}
@@ -225,26 +303,37 @@ asprintf(char **sptr, const char *fmt, ...)
@param args Arguments with values for format string % entries
*/
static void
lPrint(const char *type, SourcePos p, const char *fmt, va_list args) {
lPrint(const char *type, bool isError, SourcePos p, const char *fmt,
va_list args) {
char *errorBuf, *formattedBuf;
if (vasprintf(&errorBuf, fmt, args) == -1) {
fprintf(stderr, "vasprintf() unable to allocate memory!\n");
abort();
}
int indent = 0;
if (p.first_line == 0) {
// We don't have a valid SourcePos, so create a message without it
if (asprintf(&formattedBuf, "%s: %s\n", type, errorBuf) == -1) {
if (asprintf(&formattedBuf, "%s%s%s%s%s: %s%s\n", lStartBold(),
isError ? lStartRed() : lStartBlue(), type,
lResetColor(), lStartBold(), errorBuf,
lResetColor()) == -1) {
fprintf(stderr, "asprintf() unable to allocate memory!\n");
exit(1);
}
indent = lFindIndent(1, formattedBuf);
}
else {
// Create an error message that includes the file and line number
if (asprintf(&formattedBuf, "%s:%d:%d: %s: %s\n", p.name,
p.first_line, p.first_column, type, errorBuf) == -1) {
if (asprintf(&formattedBuf, "%s%s:%d:%d: %s%s%s%s: %s%s\n",
lStartBold(), p.name, p.first_line, p.first_column,
isError ? lStartRed() : lStartBlue(), type,
lResetColor(), lStartBold(), errorBuf,
lResetColor()) == -1) {
fprintf(stderr, "asprintf() unable to allocate memory!\n");
exit(1);
}
indent = lFindIndent(3, formattedBuf);
}
// Now that we've done all that work, see if we've already printed the
@@ -255,7 +344,7 @@ lPrint(const char *type, SourcePos p, const char *fmt, va_list args) {
return;
printed.insert(formattedBuf);
lPrintWithWordBreaks(formattedBuf, lTerminalWidth(), stderr);
lPrintWithWordBreaks(formattedBuf, indent, lTerminalWidth(), stderr);
lPrintFileLineContext(p);
free(errorBuf);
@@ -271,7 +360,7 @@ Error(SourcePos p, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
lPrint("Error", p, fmt, args);
lPrint("Error", true, p, fmt, args);
va_end(args);
}
@@ -283,7 +372,7 @@ Debug(SourcePos p, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
lPrint("Debug", p, fmt, args);
lPrint("Debug", false, p, fmt, args);
va_end(args);
}
@@ -298,7 +387,8 @@ Warning(SourcePos p, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
lPrint(g->warningsAsErrors ? "Error" : "Warning", p, fmt, args);
lPrint(g->warningsAsErrors ? "Error" : "Warning", g->warningsAsErrors,
p, fmt, args);
va_end(args);
}
@@ -311,7 +401,7 @@ PerformanceWarning(SourcePos p, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
lPrint("Performance Warning", p, fmt, args);
lPrint("Performance Warning", false, p, fmt, args);
va_end(args);
}