When available, use ANSI escapes to colorize diagnostic output.
Issue #245.
This commit is contained in:
@@ -17,6 +17,10 @@ import shlex
|
|||||||
import platform
|
import platform
|
||||||
import tempfile
|
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
|
# This script is affected by http://bugs.python.org/issue5261 on OSX 10.5 Leopard
|
||||||
# git history has a workaround for that issue.
|
# git history has a workaround for that issue.
|
||||||
|
|
||||||
|
|||||||
118
util.cpp
118
util.cpp
@@ -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
|
/** Given a pointer into a string, find the end of the current word and
|
||||||
return a pointer to its last character.
|
return a pointer to its last character.
|
||||||
*/
|
*/
|
||||||
@@ -140,17 +183,43 @@ lPrintFileLineContext(SourcePos p) {
|
|||||||
fclose(f);
|
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
|
/** Print the given string to the given FILE, assuming the given output
|
||||||
column width. Break words as needed to avoid words spilling past the
|
column width. Break words as needed to avoid words spilling past the
|
||||||
last column. */
|
last column. */
|
||||||
static void
|
static void
|
||||||
lPrintWithWordBreaks(const char *buf, int columnWidth, FILE *out) {
|
lPrintWithWordBreaks(const char *buf, int indent, int columnWidth, FILE *out) {
|
||||||
#ifdef ISPC_IS_WINDOWS
|
#ifdef ISPC_IS_WINDOWS
|
||||||
fputs(buf, out);
|
fputs(buf, out);
|
||||||
#else
|
#else
|
||||||
int column = 0;
|
int column = 0;
|
||||||
Assert(strchr(buf, ':') != NULL);
|
|
||||||
int indent = strchr(buf, ':') - buf + 2;
|
|
||||||
int width = std::max(40, columnWidth - 2);
|
int width = std::max(40, columnWidth - 2);
|
||||||
|
|
||||||
// Collect everything into a string and print it all at once at the end
|
// 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;
|
const char *msgPos = buf;
|
||||||
while (true) {
|
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))
|
while (*msgPos != '\0' && isspace(*msgPos))
|
||||||
++msgPos;
|
++msgPos;
|
||||||
if (*msgPos == '\0')
|
if (*msgPos == '\0')
|
||||||
@@ -171,8 +249,8 @@ lPrintWithWordBreaks(const char *buf, int columnWidth, FILE *out) {
|
|||||||
column = indent;
|
column = indent;
|
||||||
outStr.push_back('\n');
|
outStr.push_back('\n');
|
||||||
// Indent to the same column as the ":" at the start of the
|
// Indent to the same column as the ":" at the start of the
|
||||||
// message, unless doing so would be too far in.
|
// message.
|
||||||
for (int i = 0; i < std::min(16, indent); ++i)
|
for (int i = 0; i < indent; ++i)
|
||||||
outStr.push_back(' ');
|
outStr.push_back(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,26 +303,37 @@ asprintf(char **sptr, const char *fmt, ...)
|
|||||||
@param args Arguments with values for format string % entries
|
@param args Arguments with values for format string % entries
|
||||||
*/
|
*/
|
||||||
static void
|
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;
|
char *errorBuf, *formattedBuf;
|
||||||
if (vasprintf(&errorBuf, fmt, args) == -1) {
|
if (vasprintf(&errorBuf, fmt, args) == -1) {
|
||||||
fprintf(stderr, "vasprintf() unable to allocate memory!\n");
|
fprintf(stderr, "vasprintf() unable to allocate memory!\n");
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int indent = 0;
|
||||||
if (p.first_line == 0) {
|
if (p.first_line == 0) {
|
||||||
// We don't have a valid SourcePos, so create a message without it
|
// 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");
|
fprintf(stderr, "asprintf() unable to allocate memory!\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
indent = lFindIndent(1, formattedBuf);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Create an error message that includes the file and line number
|
// Create an error message that includes the file and line number
|
||||||
if (asprintf(&formattedBuf, "%s:%d:%d: %s: %s\n", p.name,
|
if (asprintf(&formattedBuf, "%s%s:%d:%d: %s%s%s%s: %s%s\n",
|
||||||
p.first_line, p.first_column, type, errorBuf) == -1) {
|
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");
|
fprintf(stderr, "asprintf() unable to allocate memory!\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
indent = lFindIndent(3, formattedBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that we've done all that work, see if we've already printed the
|
// 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;
|
return;
|
||||||
printed.insert(formattedBuf);
|
printed.insert(formattedBuf);
|
||||||
|
|
||||||
lPrintWithWordBreaks(formattedBuf, lTerminalWidth(), stderr);
|
lPrintWithWordBreaks(formattedBuf, indent, lTerminalWidth(), stderr);
|
||||||
lPrintFileLineContext(p);
|
lPrintFileLineContext(p);
|
||||||
|
|
||||||
free(errorBuf);
|
free(errorBuf);
|
||||||
@@ -271,7 +360,7 @@ Error(SourcePos p, const char *fmt, ...) {
|
|||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
lPrint("Error", p, fmt, args);
|
lPrint("Error", true, p, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +372,7 @@ Debug(SourcePos p, const char *fmt, ...) {
|
|||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
lPrint("Debug", p, fmt, args);
|
lPrint("Debug", false, p, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +387,8 @@ Warning(SourcePos p, const char *fmt, ...) {
|
|||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
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);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,7 +401,7 @@ PerformanceWarning(SourcePos p, const char *fmt, ...) {
|
|||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
lPrint("Performance Warning", p, fmt, args);
|
lPrint("Performance Warning", false, p, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user