[vala] Add colored output



commit 4fb521efd3ea9b38aba5ae599d55083d1c2f9a93
Author: Florian Brosch <flo brosch gmail com>
Date:   Tue Sep 2 21:17:46 2014 +0200

    Add colored output
    
    Use --no-color to disable colors
    Use VALA_COLORS to change colors
    
    E.g. VALA_COLORS = "error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01"
    
    Fixes bug 734627.

 compiler/valacompiler.vala |   21 +++-
 vala/Makefile.am           |    4 +-
 vala/valareport.vala       |  281 ++++++++++++++++++++++++++++++++++++++------
 3 files changed, 268 insertions(+), 38 deletions(-)
---
diff --git a/compiler/valacompiler.vala b/compiler/valacompiler.vala
index cbb661c..3f88124 100644
--- a/compiler/valacompiler.vala
+++ b/compiler/valacompiler.vala
@@ -24,6 +24,8 @@
 using GLib;
 
 class Vala.Compiler {
+       private const string DEFAULT_COLORS = 
"error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01";
+
        static string basedir;
        static string directory;
        static bool version;
@@ -84,6 +86,7 @@ class Vala.Compiler {
        static bool enable_version_header;
        static bool disable_version_header;
        static bool fatal_warnings;
+       static bool disable_diagnostic_colors;
        static string dependencies;
 
        static string entry_point;
@@ -140,6 +143,7 @@ class Vala.Compiler {
                { "profile", 0, 0, OptionArg.STRING, ref profile, "Use the given profile instead of the 
default", "PROFILE" },
                { "quiet", 'q', 0, OptionArg.NONE, ref quiet_mode, "Do not print messages to the console", 
null },
                { "verbose", 'v', 0, OptionArg.NONE, ref verbose_mode, "Print additional messages to the 
console", null },
+               { "no-color", 0, 0, OptionArg.NONE, ref disable_diagnostic_colors, "Disable colored output", 
null },
                { "target-glib", 0, 0, OptionArg.STRING, ref target_glib, "Target version of glib for code 
generation", "MAJOR.MINOR" },
                { "gresources", 0, 0, OptionArg.STRING_ARRAY, ref gresources, "XML of gresources", "FILE..." 
},
                { "enable-version-header", 0, 0, OptionArg.NONE, ref enable_version_header, "Write vala build 
version in generated files", null },
@@ -147,7 +151,7 @@ class Vala.Compiler {
                { "", 0, 0, OptionArg.FILENAME_ARRAY, ref sources, null, "FILE..." },
                { null }
        };
-       
+
        private int quit () {
                if (context.report.get_errors () == 0 && context.report.get_warnings () == 0) {
                        return 0;
@@ -169,6 +173,17 @@ class Vala.Compiler {
                context = new CodeContext ();
                CodeContext.push (context);
 
+               if (disable_diagnostic_colors == false) {
+                       string[] env_args = Environ.get ();
+                       unowned string env_colors = Environ.get_variable (env_args, "VALA_COLORS");
+                       if (env_colors != null) {
+                               context.report.set_colors (env_colors);
+                       } else {
+                               context.report.set_colors (DEFAULT_COLORS);
+                       }
+               }
+
+
                // default to build executable
                if (!ccode_only && !compile_only && output == null) {
                        // strip extension if there is one
@@ -292,7 +307,7 @@ class Vala.Compiler {
                }
 
                context.gresources = gresources;
-               
+
                if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 
0)) {
                        return quit ();
                }
@@ -319,7 +334,7 @@ class Vala.Compiler {
                if (context.report.get_errors () > 0 || (fatal_warnings && context.report.get_warnings () > 
0)) {
                        return quit ();
                }
-               
+
                var parser = new Parser ();
                parser.parse (context);
 
diff --git a/vala/Makefile.am b/vala/Makefile.am
index 54d01bd..c7fbc0e 100644
--- a/vala/Makefile.am
+++ b/vala/Makefile.am
@@ -6,6 +6,7 @@ AM_CPPFLAGS = \
        $(COVERAGE_CFLAGS) \
        -I$(top_srcdir)/gee \
        $(GLIB_CFLAGS) \
+       $(GMODULE_CFLAGS) \
        -DPACKAGE_DATADIR=\"$(pkgdatadir)\" \
        $(NULL)
 
@@ -179,7 +180,7 @@ vala.vapi vala.vala.stamp: $(libvalacore_la_VALASOURCES)
                $(COVERAGE_VALAFLAGS) \
                $(VALAFLAGS) \
                -C \
-               --vapidir $(top_srcdir)/vapi --pkg gobject-2.0 \
+               --vapidir $(top_srcdir)/vapi --pkg gmodule-2.0 --pkg gobject-2.0 \
                --vapidir $(top_srcdir)/gee --pkg gee \
                --pkg config \
                -H vala.h \
@@ -190,6 +191,7 @@ vala.vapi vala.vala.stamp: $(libvalacore_la_VALASOURCES)
 libvalacore_la_LIBADD = \
        $(COVERAGE_LIBS) \
        $(GLIB_LIBS) \
+       $(GMODULE_LIBS) \
        $(top_builddir)/gee/libgee.la \
        $(NULL)
 
diff --git a/vala/valareport.vala b/vala/valareport.vala
index 18169c5..67b3a80 100644
--- a/vala/valareport.vala
+++ b/vala/valareport.vala
@@ -22,17 +22,184 @@
 
 using GLib;
 
+
 /**
  * Namespace to centralize reporting warnings and errors.
  */
 public class Vala.Report : Object {
+       /**
+        * SGR end tag
+        */
+       private const string ANSI_COLOR_END = "\x1b[0m";
+
+       /**
+        * SGR start tag for source location
+        */
+       private string locus_color_start = "";
+
+       /**
+        * SGR end tag for source location
+        */
+       private unowned string locus_color_end = "";
+
+       /**
+        * SGR start tag for warning titles
+        */
+       private string warning_color_start = "";
+
+       /**
+        * SGR end tag for warning titles
+        */
+       private unowned string warning_color_end = "";
+
+       /**
+        * SGR start tag for error titles
+        */
+       private string error_color_start = "";
+
+       /**
+        * SGR end tag for error titles
+        */
+       private unowned string error_color_end = "";
+
+       /**
+        * SGR start tag for note titles
+        */
+       private string note_color_start = "";
+
+       /**
+        * SGR end tag for note titles
+        */
+       private unowned string note_color_end = "";
+
+       /**
+        * SGR start tag for caret line (^^^)
+        */
+       private string caret_color_start = "";
+
+       /**
+        * SGR end tag for caret line (^^^)
+        */
+       private unowned string caret_color_end = "";
+
+       /**
+        * SGR start tag for quotes line ('', ``, `')
+        */
+       private string quote_color_start = "";
+
+       /**
+        * SGR end tag for quotes line ('', ``, `')
+        */
+       private unowned string quote_color_end = "";
+
+
        protected int warnings;
        protected int errors;
 
-       bool verbose_errors;
+       private bool verbose_errors;
 
        public bool enable_warnings { get; set; default = true; }
 
+
+       /**
+        * Set all colors by string
+        *
+        * {{{
+        *   "error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01"
+        * }}}
+        */
+       public bool set_colors (string str) {
+               Regex val_regex;
+               try {
+                       val_regex = new Regex ("^\\s*[0-9]+(;[0-9]*)*\\s*$");
+               } catch (RegexError e) {
+                       assert_not_reached ();
+               }
+
+               string error_color = null;
+               string warning_color = null;
+               string note_color = null;
+               string caret_color = null;
+               string locus_color = null;
+               string quote_color = null;
+
+               string[] fragments = str.split (":");
+               foreach (unowned string fragment in fragments) {
+                       string[] eq = fragment.split ("=", 2);
+                       if (eq.length != 2) {
+                               return false;
+                       }
+
+                       if (!val_regex.match (eq[1])) {
+                               return false;
+                       }
+
+
+                       unowned string checked_value = eq[1]._strip ();
+                       switch (eq[0]._strip ()) {
+                       case "error":
+                               error_color = checked_value;
+                               break;
+
+                       case "warning":
+                               warning_color = checked_value;
+                               break;
+
+                       case "note":
+                               note_color = checked_value;
+                               break;
+
+                       case "caret":
+                               caret_color = checked_value;
+                               break;
+
+                       case "locus":
+                               locus_color = checked_value;
+                               break;
+
+                       case "quote":
+                               quote_color = checked_value;
+                               break;
+
+                       default:
+                               return false;
+                       }
+               }
+
+               if (is_atty (stderr.fileno ())) {
+                       if (error_color != null) {
+                               this.error_color_start = "\x1b[0" + error_color + "m";
+                               this.error_color_end = ANSI_COLOR_END;
+                       }
+
+                       if (warning_color != null) {
+                               this.warning_color_start = "\x1b[0" + warning_color + "m";
+                               this.warning_color_end = ANSI_COLOR_END;
+                       }
+
+                       if (note_color != null) {
+                               this.note_color_start = "\x1b[0" + note_color + "m";
+                               this.note_color_end = ANSI_COLOR_END;
+                       }
+
+                       if (caret_color != null) {
+                               this.caret_color_start = "\x1b[0" + caret_color + "m";
+                               this.caret_color_end = ANSI_COLOR_END;
+                       }
+
+                       if (locus_color != null) {
+                               this.locus_color_start = "\x1b[0" + locus_color + "m";
+                               this.locus_color_end = ANSI_COLOR_END;
+                       }
+
+                       if (quote_color != null) {
+                               this.quote_color_start = "\x1b[0" + quote_color + "m";
+                               this.quote_color_end = ANSI_COLOR_END;
+                       }
+               }
+               return true;
+       }
+
        /**
         * Set the error verbosity.
         */
@@ -46,7 +213,7 @@ public class Vala.Report : Object {
        public int get_warnings () {
                return warnings;
        }
-       
+
        /**
         * Returns the total number of errors reported.
         */
@@ -57,7 +224,7 @@ public class Vala.Report : Object {
        /**
         * Pretty-print the actual line of offending code if possible.
         */
-       static void report_source (SourceReference source) {
+       private void report_source (SourceReference source) {
                if (source.begin.line != source.end.line) {
                        // FIXME Cannot report multi-line issues currently
                        return;
@@ -68,7 +235,7 @@ public class Vala.Report : Object {
                if (offending_line != null) {
                        stderr.printf ("%s\n", offending_line);
                        int idx;
-                       
+
                        /* We loop in this manner so that we don't fall over on differing
                         * tab widths. This means we get the ^s in the right places.
                         */
@@ -80,6 +247,7 @@ public class Vala.Report : Object {
                                }
                        }
 
+                       stderr.puts (caret_color_start);
                        for (idx = source.begin.column; idx <= source.end.column; ++idx) {
                                if (offending_line[idx - 1] == '\t') {
                                        stderr.printf ("\t");
@@ -87,11 +255,58 @@ public class Vala.Report : Object {
                                        stderr.printf ("^");
                                }
                        }
+                       stderr.puts (caret_color_end);
 
                        stderr.printf ("\n");
                }
        }
 
+       private void print_highlighted_message (string message) {
+               int start = 0;
+               int cur = 0;
+
+               while (message[cur] != '\0') {
+                       if (message[cur] == '\'' || message[cur] == '`') {
+                               unowned string end_chars = (message[cur] == '`')? "`'" : "'";
+                               stderr.puts (message.substring (start, cur - start));
+                               start = cur;
+                               cur++;
+
+                               while (message[cur] != '\0' && end_chars.index_of_char (message[cur]) < 0) {
+                                       cur++;
+                               }
+                               if (message[cur] == '\0') {
+                                       stderr.puts (message.substring (start, cur - start));
+                                       start = cur;
+                               } else {
+                                       cur++;
+                                       stderr.printf ("%s%s%s", quote_color_start, message.substring (start, 
cur - start), quote_color_end);
+                                       start = cur;
+                               }
+                       } else {
+                               cur++;
+                       }
+               }
+
+               stderr.puts (message.offset (start));
+       }
+
+       private void print_message (SourceReference? source, string type, string type_color_start, string 
type_color_end, string message, bool do_report_source) {
+               if (source != null) {
+                       stderr.printf ("%s%s:%s ", locus_color_start, source.to_string (), locus_color_end);
+               }
+
+               stderr.printf ("%s%s:%s ", type_color_start, type, type_color_end);
+
+               // highlight '', `', ``
+               print_highlighted_message (message);
+               stderr.putc ('\n');
+
+               if (do_report_source && source != null) {
+                       report_source (source);
+               }
+       }
+
        /**
         * Reports the specified message as note.
         *
@@ -103,14 +318,7 @@ public class Vala.Report : Object {
                        return;
                }
 
-               if (source == null) {
-                       stderr.printf ("note: %s\n", message);
-               } else {
-                       stderr.printf ("%s: note: %s\n", source.to_string (), message);
-                       if (verbose_errors) {
-                               report_source (source);
-                       }
-               }
+               print_message (source, "note", note_color_start, note_color_end, message, verbose_errors);
        }
 
        /**
@@ -125,11 +333,8 @@ public class Vala.Report : Object {
                }
 
                warnings++;
-               if (source == null) {
-                       stderr.printf ("warning: %s\n", message);
-               } else {
-                       stderr.printf ("%s: warning: %s\n", source.to_string (), message);
-               }
+
+               print_message (source, "warning", warning_color_start, warning_color_end, message, false);
        }
 
        /**
@@ -144,16 +349,10 @@ public class Vala.Report : Object {
                }
 
                warnings++;
-               if (source == null) {
-                       stderr.printf ("warning: %s\n", message);
-               } else {
-                       stderr.printf ("%s: warning: %s\n", source.to_string (), message);
-                       if (verbose_errors) {
-                               report_source (source);
-                       }
-               }
+
+               print_message (source, "warning", warning_color_start, warning_color_end, message, 
verbose_errors);
        }
-       
+
        /**
         * Reports the specified message as error.
         *
@@ -162,14 +361,8 @@ public class Vala.Report : Object {
         */
        public virtual void err (SourceReference? source, string message) {
                errors++;
-               if (source == null) {
-                       stderr.printf ("error: %s\n", message);
-               } else {
-                       stderr.printf ("%s: error: %s\n", source.to_string (), message);
-                       if (verbose_errors) {
-                               report_source (source);
-                       }
-               }
+
+               print_message (source, "error", error_color_start, error_color_end, message, verbose_errors);
        }
 
        /* Convenience methods calling warn and err on correct instance */
@@ -188,4 +381,24 @@ public class Vala.Report : Object {
        public static void error (SourceReference? source, string message) {
                CodeContext.get ().report.err (source, message);
        }
+
+
+       [CCode (has_target = false)]
+       private delegate int AttyFunc (int fd);
+
+       private bool is_atty (int fd) {
+               Module module = Module.open (null, ModuleFlags.BIND_LAZY); 
+               if (module == null) {
+                       return false;
+               }
+
+               void* _func;
+               module.symbol ("isatty", out _func);
+               if (_func == null) {
+                       return false;
+               }
+
+               AttyFunc? func = (AttyFunc) _func;
+               return func (fd) == 1;
+       }
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]