From 6aa54cbe9a19f84e8089e288622b7ed81cff7928 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 22 Feb 2023 12:31:51 +0000 Subject: [PATCH] Begin work on adding colours to error messages on Windows Terminals --- src/build_settings.cpp | 4 ++ src/error.cpp | 119 +++++++++++++++++++++++++++++++++-------- src/main.cpp | 15 ++++++ 3 files changed, 116 insertions(+), 22 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index ff372f418..562b669b5 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -289,6 +289,7 @@ struct BuildContext { bool ignore_warnings; bool warnings_as_errors; bool hide_error_line; + bool has_ansi_terminal_colours; bool ignore_lazy; bool ignore_llvm_build; @@ -1035,6 +1036,9 @@ gb_internal String get_fullpath_core(gbAllocator a, String path) { gb_internal bool show_error_line(void) { return !build_context.hide_error_line; } +gb_internal bool has_ansi_terminal_colours(void) { + return build_context.has_ansi_terminal_colours; +} gb_internal bool has_asm_extension(String const &path) { String ext = path_extension(path); diff --git a/src/error.cpp b/src/error.cpp index cb2aa4836..750cd147f 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -101,6 +101,7 @@ gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) { gb_internal bool global_warnings_as_errors(void); gb_internal bool global_ignore_warnings(void); gb_internal bool show_error_line(void); +gb_internal bool has_ansi_terminal_colours(void); gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset); gb_internal void warning(Token const &token, char const *fmt, ...); @@ -190,10 +191,8 @@ gb_internal ERROR_OUT_PROC(default_error_out_va) { gb_file_write(f, buf, n); } - gb_global ErrorOutProc *error_out_va = default_error_out_va; - gb_internal void error_out(char const *fmt, ...) { va_list va; va_start(va, fmt); @@ -201,6 +200,49 @@ gb_internal void error_out(char const *fmt, ...) { va_end(va); } +enum TerminalStyle { + TerminalStyle_Normal, + TerminalStyle_Bold, + TerminalStyle_Underline, +}; + +enum TerminalColour { + TerminalColour_White, + TerminalColour_Red, + TerminalColour_Yellow, + TerminalColour_Green, + TerminalColour_Cyan, + TerminalColour_Blue, + TerminalColour_Purple, + TerminalColour_Black, +}; + +gb_internal void terminal_set_colours(TerminalStyle style, TerminalColour foreground) { + if (has_ansi_terminal_colours()) { + char const *ss = "0"; + switch (style) { + case TerminalStyle_Normal: ss = "0"; break; + case TerminalStyle_Bold: ss = "1"; break; + case TerminalStyle_Underline: ss = "4"; break; + } + switch (foreground) { + case TerminalColour_White: error_out("\x1b[%s;37m", ss); break; + case TerminalColour_Red: error_out("\x1b[%s;31m", ss); break; + case TerminalColour_Yellow: error_out("\x1b[%s;33m", ss); break; + case TerminalColour_Green: error_out("\x1b[%s;32m", ss); break; + case TerminalColour_Cyan: error_out("\x1b[%s;36m", ss); break; + case TerminalColour_Blue: error_out("\x1b[%s;34m", ss); break; + case TerminalColour_Purple: error_out("\x1b[%s;35m", ss); break; + case TerminalColour_Black: error_out("\x1b[%s;30m", ss); break; + } + } +} +gb_internal void terminal_reset_colours(void) { + if (has_ansi_terminal_colours()) { + error_out("\x1b[0m"); + } +} + gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { if (!show_error_line()) { @@ -223,6 +265,9 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { }; error_out("\t"); + + terminal_set_colours(TerminalStyle_Bold, TerminalColour_White); + if (line.len+MAX_TAB_WIDTH+ELLIPSIS_PADDING > MAX_LINE_LENGTH) { i32 const half_width = MAX_LINE_LENGTH/2; i32 left = cast(i32)(offset); @@ -246,6 +291,9 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { for (i32 i = 0; i < offset; i++) { error_out(" "); } + + terminal_set_colours(TerminalStyle_Bold, TerminalColour_Green); + error_out("^"); if (end.file_id == pos.file_id) { if (end.line > pos.line) { @@ -263,24 +311,42 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { } } + terminal_reset_colours(); + error_out("\n"); return true; } return false; } +gb_internal void error_out_pos(TokenPos pos) { + terminal_set_colours(TerminalStyle_Bold, TerminalColour_White); + error_out("%s ", token_pos_to_string(pos)); + terminal_reset_colours(); +} + +gb_internal void error_out_coloured(char const *str, TerminalStyle style, TerminalColour foreground) { + terminal_set_colours(style, foreground); + error_out(str); + terminal_reset_colours(); +} + + + gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { global_error_collector.count.fetch_add(1); mutex_lock(&global_error_collector.mutex); // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { - error_out("Error: %s\n", gb_bprintf_va(fmt, va)); + error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); + error_out_va(fmt, va); + error_out("\n"); } else if (global_error_collector.prev != pos) { global_error_collector.prev = pos; - error_out("%s %s\n", - token_pos_to_string(pos), - gb_bprintf_va(fmt, va)); + error_out_pos(pos); + error_out_va(fmt, va); + error_out("\n"); show_error_on_line(pos, end); } mutex_unlock(&global_error_collector.mutex); @@ -299,12 +365,15 @@ gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, if (!global_ignore_warnings()) { // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { - error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); + error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); + error_out_va(fmt, va); + error_out("\n"); } else if (global_error_collector.prev != pos) { global_error_collector.prev = pos; - error_out("%s Warning: %s\n", - token_pos_to_string(pos), - gb_bprintf_va(fmt, va)); + error_out_pos(pos); + error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); + error_out_va(fmt, va); + error_out("\n"); show_error_on_line(pos, end); } } @@ -321,12 +390,12 @@ gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_li global_error_collector.count++; // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { - error_out("Error: %s", gb_bprintf_va(fmt, va)); + error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); + error_out_va(fmt, va); } else if (global_error_collector.prev != pos) { global_error_collector.prev = pos; - error_out("%s %s", - token_pos_to_string(pos), - gb_bprintf_va(fmt, va)); + error_out_pos(pos); + error_out_va(fmt, va); } mutex_unlock(&global_error_collector.mutex); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) { @@ -341,12 +410,15 @@ gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const * // NOTE(bill): Duplicate error, skip it if (global_error_collector.prev != pos) { global_error_collector.prev = pos; - error_out("%s Syntax Error: %s\n", - token_pos_to_string(pos), - gb_bprintf_va(fmt, va)); + error_out_pos(pos); + error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red); + error_out_va(fmt, va); + error_out("\n"); show_error_on_line(pos, end); } else if (pos.line == 0) { - error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va)); + error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red); + error_out_va(fmt, va); + error_out("\n"); } mutex_unlock(&global_error_collector.mutex); @@ -366,12 +438,15 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const // NOTE(bill): Duplicate error, skip it if (global_error_collector.prev != pos) { global_error_collector.prev = pos; - error_out("%s Syntax Warning: %s\n", - token_pos_to_string(pos), - gb_bprintf_va(fmt, va)); + error_out_pos(pos); + error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); + error_out_va(fmt, va); + error_out("\n"); show_error_on_line(pos, end); } else if (pos.line == 0) { - error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); + error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); + error_out_va(fmt, va); + error_out("\n"); } } mutex_unlock(&global_error_collector.mutex); diff --git a/src/main.cpp b/src/main.cpp index 02996065a..ca91bb8f5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2494,6 +2494,20 @@ gb_internal int strip_semicolons(Parser *parser) { return cast(int)failed; } +gb_internal void init_terminal(void) { + build_context.has_ansi_terminal_colours = false; +#if defined(GB_SYSTEM_WINDOWS) + HANDLE hnd = GetStdHandle(STD_ERROR_HANDLE); + DWORD mode = 0; + if (GetConsoleMode(hnd, &mode)) { + enum {FLAG_ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004}; + if (SetConsoleMode(hnd, mode|FLAG_ENABLE_VIRTUAL_TERMINAL_PROCESSING)) { + build_context.has_ansi_terminal_colours = true; + } + } +#endif +} + int main(int arg_count, char const **arg_ptr) { if (arg_count < 2) { usage(make_string_c(arg_ptr[0])); @@ -2509,6 +2523,7 @@ int main(int arg_count, char const **arg_ptr) { init_string_interner(); init_global_error_collector(); init_keyword_hash_table(); + init_terminal(); if (!check_env()) { return 1;