3 * Copyright (C) 2006-2010 Jürg Billeter
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 * Jürg Billeter <j@bitron.ch>
27 * Namespace to centralize reporting warnings and errors.
29 public class Vala
.Report
{
37 * SGR (Select Graphic Rendition) end tag
39 private const string ANSI_COLOR_END
= "\x1b[0m";
42 * SGR (Select Graphic Rendition) start tag for source location
44 private string locus_color_start
= "";
47 * SGR (Select Graphic Rendition) end tag for source location
49 private unowned
string locus_color_end
= "";
52 * SGR (Select Graphic Rendition) start tag for warning titles
54 private string warning_color_start
= "";
57 * SGR (Select Graphic Rendition) end tag for warning titles
59 private unowned
string warning_color_end
= "";
62 * SGR (Select Graphic Rendition) start tag for error titles
64 private string error_color_start
= "";
67 * SGR (Select Graphic Rendition) end tag for error titles
69 private unowned
string error_color_end
= "";
72 * SGR (Select Graphic Rendition) start tag for note titles
74 private string note_color_start
= "";
77 * SGR (Select Graphic Rendition) end tag for note titles
79 private unowned
string note_color_end
= "";
82 * SGR (Select Graphic Rendition) start tag for caret line (^^^)
84 private string caret_color_start
= "";
87 * SGR (Select Graphic Rendition) end tag for caret line (^^^)
89 private unowned
string caret_color_end
= "";
92 * SGR (Select Graphic Rendition) start tag for quotes line ('...', `...`, `...')
94 private string quote_color_start
= "";
97 * SGR (Select Graphic Rendition) end tag for quotes line ('...', `...`, `...')
99 private unowned
string quote_color_end
= "";
102 protected int warnings
;
103 protected int errors
;
105 private bool verbose_errors
;
107 public bool enable_warnings
{ get; set; default = true; }
109 static GLib
.Regex val_regex
;
112 * Set all colors by string
115 * "error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01"
118 public bool set_colors (string str
, Report
.Colored colored_output
= Report
.Colored
.AUTO
) {
120 if (val_regex
== null)
121 val_regex
= new
Regex ("^\\s*[0-9]+(;[0-9]*)*\\s*$");
122 } catch (RegexError e
) {
123 assert_not_reached ();
126 string error_color
= null;
127 string warning_color
= null;
128 string note_color
= null;
129 string caret_color
= null;
130 string locus_color
= null;
131 string quote_color
= null;
133 string[] fragments
= str
.split (":");
134 foreach (unowned
string fragment
in fragments
) {
135 string[] eq
= fragment
.split ("=", 2);
136 if (eq
.length
!= 2) {
140 if (!val_regex
.match (eq
[1])) {
145 unowned
string checked_value
= eq
[1]._strip ();
146 switch (eq
[0]._strip ()) {
148 error_color
= checked_value
;
152 warning_color
= checked_value
;
156 note_color
= checked_value
;
160 caret_color
= checked_value
;
164 locus_color
= checked_value
;
168 quote_color
= checked_value
;
176 if (colored_output
== Report
.Colored
.ALWAYS
|| (colored_output
== Report
.Colored
.AUTO
&& is_atty (stderr
.fileno ()))) {
177 if (error_color
!= null) {
178 this
.error_color_start
= "\x1b[0" + error_color
+ "m";
179 this
.error_color_end
= ANSI_COLOR_END
;
182 if (warning_color
!= null) {
183 this
.warning_color_start
= "\x1b[0" + warning_color
+ "m";
184 this
.warning_color_end
= ANSI_COLOR_END
;
187 if (note_color
!= null) {
188 this
.note_color_start
= "\x1b[0" + note_color
+ "m";
189 this
.note_color_end
= ANSI_COLOR_END
;
192 if (caret_color
!= null) {
193 this
.caret_color_start
= "\x1b[0" + caret_color
+ "m";
194 this
.caret_color_end
= ANSI_COLOR_END
;
197 if (locus_color
!= null) {
198 this
.locus_color_start
= "\x1b[0" + locus_color
+ "m";
199 this
.locus_color_end
= ANSI_COLOR_END
;
202 if (quote_color
!= null) {
203 this
.quote_color_start
= "\x1b[0" + quote_color
+ "m";
204 this
.quote_color_end
= ANSI_COLOR_END
;
211 * Set the error verbosity.
213 public void set_verbose_errors (bool verbose
) {
214 verbose_errors
= verbose
;
218 * Returns the total number of warnings reported.
220 public int get_warnings () {
225 * Returns the total number of errors reported.
227 public int get_errors () {
232 * Pretty-print the actual line of offending code if possible.
234 private void report_source (SourceReference source
) {
235 if (source
.begin
.line
!= source
.end
.line
) {
236 // FIXME Cannot report multi-line issues currently
240 string offending_line
= source
.file
.get_source_line (source
.begin
.line
);
242 if (offending_line
!= null) {
243 stderr
.printf ("%s\n", offending_line
);
246 /* We loop in this manner so that we don't fall over on differing
247 * tab widths. This means we get the ^s in the right places.
249 for (idx
= 1; idx
< source
.begin
.column
; ++idx
) {
250 if (offending_line
[idx
- 1] == '\t') {
251 stderr
.printf ("\t");
257 stderr
.puts (caret_color_start
);
258 for (idx
= source
.begin
.column
; idx
<= source
.end
.column
; ++idx
) {
259 if (offending_line
[idx
- 1] == '\t') {
260 stderr
.printf ("\t");
265 stderr
.puts (caret_color_end
);
267 stderr
.printf ("\n");
271 private void print_highlighted_message (string message
) {
275 while (message
[cur
] != '\0') {
276 if (message
[cur
] == '\'' || message
[cur
] == '`') {
277 unowned
string end_chars
= (message
[cur
] == '`')?
"`'" : "'";
278 stderr
.puts (message
.substring (start
, cur
- start
));
282 while (message
[cur
] != '\0' && end_chars
.index_of_char (message
[cur
]) < 0) {
285 if (message
[cur
] == '\0') {
286 stderr
.puts (message
.substring (start
, cur
- start
));
290 stderr
.printf ("%s%s%s", quote_color_start
, message
.substring (start
, cur
- start
), quote_color_end
);
298 stderr
.puts (message
.offset (start
));
301 private void print_message (SourceReference? source
, string type
, string type_color_start
, string type_color_end
, string message
, bool do_report_source
) {
302 if (source
!= null) {
303 stderr
.printf ("%s%s:%s ", locus_color_start
, source
.to_string (), locus_color_end
);
306 stderr
.printf ("%s%s:%s ", type_color_start
, type
, type_color_end
);
308 // highlight '', `', ``
309 print_highlighted_message (message
);
312 if (do_report_source
&& source
!= null) {
313 report_source (source
);
318 * Reports the specified message as note.
320 * @param source reference to source code
321 * @param message note message
323 public virtual void note (SourceReference? source
, string message
) {
324 if (!enable_warnings
) {
328 print_message (source
, "note", note_color_start
, note_color_end
, message
, verbose_errors
);
332 * Reports the specified message as deprecation warning.
334 * @param source reference to source code
335 * @param message warning message
337 public virtual void depr (SourceReference? source
, string message
) {
338 if (!enable_warnings
) {
344 print_message (source
, "warning", warning_color_start
, warning_color_end
, message
, false);
348 * Reports the specified message as warning.
350 * @param source reference to source code
351 * @param message warning message
353 public virtual void warn (SourceReference? source
, string message
) {
354 if (!enable_warnings
) {
360 print_message (source
, "warning", warning_color_start
, warning_color_end
, message
, verbose_errors
);
364 * Reports the specified message as error.
366 * @param source reference to source code
367 * @param message error message
369 public virtual void err (SourceReference? source
, string message
) {
372 print_message (source
, "error", error_color_start
, error_color_end
, message
, verbose_errors
);
375 /* Convenience methods calling warn and err on correct instance */
376 public static void notice (SourceReference? source
, string message
) {
377 CodeContext
.get ().report
.note (source
, message
);
379 public static void deprecated (SourceReference? source
, string message
) {
380 CodeContext
.get ().report
.depr (source
, message
);
382 public static void experimental (SourceReference? source
, string message
) {
383 CodeContext
.get ().report
.depr (source
, message
);
385 public static void warning (SourceReference? source
, string message
) {
386 CodeContext
.get ().report
.warn (source
, message
);
388 public static void error (SourceReference? source
, string message
) {
389 CodeContext
.get ().report
.err (source
, message
);
393 [CCode (has_target
= false)]
394 private delegate
int AttyFunc (int fd
);
396 private bool is_atty (int fd
) {
397 Module module
= Module
.open (null, ModuleFlags
.BIND_LAZY
);
398 if (module
== null) {
403 module
.symbol ("isatty", out _func
);
408 AttyFunc? func
= (AttyFunc
) _func
;
409 return func (fd
) == 1;