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
: Object
{
31 * SGR (Select Graphic Rendition) end tag
33 private const string ANSI_COLOR_END
= "\x1b[0m";
36 * SGR (Select Graphic Rendition) start tag for source location
38 private string locus_color_start
= "";
41 * SGR (Select Graphic Rendition) end tag for source location
43 private unowned
string locus_color_end
= "";
46 * SGR (Select Graphic Rendition) start tag for warning titles
48 private string warning_color_start
= "";
51 * SGR (Select Graphic Rendition) end tag for warning titles
53 private unowned
string warning_color_end
= "";
56 * SGR (Select Graphic Rendition) start tag for error titles
58 private string error_color_start
= "";
61 * SGR (Select Graphic Rendition) end tag for error titles
63 private unowned
string error_color_end
= "";
66 * SGR (Select Graphic Rendition) start tag for note titles
68 private string note_color_start
= "";
71 * SGR (Select Graphic Rendition) end tag for note titles
73 private unowned
string note_color_end
= "";
76 * SGR (Select Graphic Rendition) start tag for caret line (^^^)
78 private string caret_color_start
= "";
81 * SGR (Select Graphic Rendition) end tag for caret line (^^^)
83 private unowned
string caret_color_end
= "";
86 * SGR (Select Graphic Rendition) start tag for quotes line ('...', `...`, `...')
88 private string quote_color_start
= "";
91 * SGR (Select Graphic Rendition) end tag for quotes line ('...', `...`, `...')
93 private unowned
string quote_color_end
= "";
96 protected int warnings
;
99 private bool verbose_errors
;
101 public bool enable_warnings
{ get; set; default = true; }
103 static GLib
.Regex val_regex
;
106 * Set all colors by string
109 * "error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01"
112 public bool set_colors (string str
) {
114 if (val_regex
== null)
115 val_regex
= new
Regex ("^\\s*[0-9]+(;[0-9]*)*\\s*$");
116 } catch (RegexError e
) {
117 assert_not_reached ();
120 string error_color
= null;
121 string warning_color
= null;
122 string note_color
= null;
123 string caret_color
= null;
124 string locus_color
= null;
125 string quote_color
= null;
127 string[] fragments
= str
.split (":");
128 foreach (unowned
string fragment
in fragments
) {
129 string[] eq
= fragment
.split ("=", 2);
130 if (eq
.length
!= 2) {
134 if (!val_regex
.match (eq
[1])) {
139 unowned
string checked_value
= eq
[1]._strip ();
140 switch (eq
[0]._strip ()) {
142 error_color
= checked_value
;
146 warning_color
= checked_value
;
150 note_color
= checked_value
;
154 caret_color
= checked_value
;
158 locus_color
= checked_value
;
162 quote_color
= checked_value
;
170 if (is_atty (stderr
.fileno ())) {
171 if (error_color
!= null) {
172 this
.error_color_start
= "\x1b[0" + error_color
+ "m";
173 this
.error_color_end
= ANSI_COLOR_END
;
176 if (warning_color
!= null) {
177 this
.warning_color_start
= "\x1b[0" + warning_color
+ "m";
178 this
.warning_color_end
= ANSI_COLOR_END
;
181 if (note_color
!= null) {
182 this
.note_color_start
= "\x1b[0" + note_color
+ "m";
183 this
.note_color_end
= ANSI_COLOR_END
;
186 if (caret_color
!= null) {
187 this
.caret_color_start
= "\x1b[0" + caret_color
+ "m";
188 this
.caret_color_end
= ANSI_COLOR_END
;
191 if (locus_color
!= null) {
192 this
.locus_color_start
= "\x1b[0" + locus_color
+ "m";
193 this
.locus_color_end
= ANSI_COLOR_END
;
196 if (quote_color
!= null) {
197 this
.quote_color_start
= "\x1b[0" + quote_color
+ "m";
198 this
.quote_color_end
= ANSI_COLOR_END
;
205 * Set the error verbosity.
207 public void set_verbose_errors (bool verbose
) {
208 verbose_errors
= verbose
;
212 * Returns the total number of warnings reported.
214 public int get_warnings () {
219 * Returns the total number of errors reported.
221 public int get_errors () {
226 * Pretty-print the actual line of offending code if possible.
228 private void report_source (SourceReference source
) {
229 if (source
.begin
.line
!= source
.end
.line
) {
230 // FIXME Cannot report multi-line issues currently
234 string offending_line
= source
.file
.get_source_line (source
.begin
.line
);
236 if (offending_line
!= null) {
237 stderr
.printf ("%s\n", offending_line
);
240 /* We loop in this manner so that we don't fall over on differing
241 * tab widths. This means we get the ^s in the right places.
243 for (idx
= 1; idx
< source
.begin
.column
; ++idx
) {
244 if (offending_line
[idx
- 1] == '\t') {
245 stderr
.printf ("\t");
251 stderr
.puts (caret_color_start
);
252 for (idx
= source
.begin
.column
; idx
<= source
.end
.column
; ++idx
) {
253 if (offending_line
[idx
- 1] == '\t') {
254 stderr
.printf ("\t");
259 stderr
.puts (caret_color_end
);
261 stderr
.printf ("\n");
265 private void print_highlighted_message (string message
) {
269 while (message
[cur
] != '\0') {
270 if (message
[cur
] == '\'' || message
[cur
] == '`') {
271 unowned
string end_chars
= (message
[cur
] == '`')?
"`'" : "'";
272 stderr
.puts (message
.substring (start
, cur
- start
));
276 while (message
[cur
] != '\0' && end_chars
.index_of_char (message
[cur
]) < 0) {
279 if (message
[cur
] == '\0') {
280 stderr
.puts (message
.substring (start
, cur
- start
));
284 stderr
.printf ("%s%s%s", quote_color_start
, message
.substring (start
, cur
- start
), quote_color_end
);
292 stderr
.puts (message
.offset (start
));
295 private void print_message (SourceReference? source
, string type
, string type_color_start
, string type_color_end
, string message
, bool do_report_source
) {
296 if (source
!= null) {
297 stderr
.printf ("%s%s:%s ", locus_color_start
, source
.to_string (), locus_color_end
);
300 stderr
.printf ("%s%s:%s ", type_color_start
, type
, type_color_end
);
302 // highlight '', `', ``
303 print_highlighted_message (message
);
306 if (do_report_source
&& source
!= null) {
307 report_source (source
);
312 * Reports the specified message as note.
314 * @param source reference to source code
315 * @param message note message
317 public virtual void note (SourceReference? source
, string message
) {
318 if (!enable_warnings
) {
322 print_message (source
, "note", note_color_start
, note_color_end
, message
, verbose_errors
);
326 * Reports the specified message as deprecation warning.
328 * @param source reference to source code
329 * @param message warning message
331 public virtual void depr (SourceReference? source
, string message
) {
332 if (!enable_warnings
) {
338 print_message (source
, "warning", warning_color_start
, warning_color_end
, message
, false);
342 * Reports the specified message as warning.
344 * @param source reference to source code
345 * @param message warning message
347 public virtual void warn (SourceReference? source
, string message
) {
348 if (!enable_warnings
) {
354 print_message (source
, "warning", warning_color_start
, warning_color_end
, message
, verbose_errors
);
358 * Reports the specified message as error.
360 * @param source reference to source code
361 * @param message error message
363 public virtual void err (SourceReference? source
, string message
) {
366 print_message (source
, "error", error_color_start
, error_color_end
, message
, verbose_errors
);
369 /* Convenience methods calling warn and err on correct instance */
370 public static void notice (SourceReference? source
, string message
) {
371 CodeContext
.get ().report
.note (source
, message
);
373 public static void deprecated (SourceReference? source
, string message
) {
374 CodeContext
.get ().report
.depr (source
, message
);
376 public static void experimental (SourceReference? source
, string message
) {
377 CodeContext
.get ().report
.depr (source
, message
);
379 public static void warning (SourceReference? source
, string message
) {
380 CodeContext
.get ().report
.warn (source
, message
);
382 public static void error (SourceReference? source
, string message
) {
383 CodeContext
.get ().report
.err (source
, message
);
387 [CCode (has_target
= false)]
388 private delegate
int AttyFunc (int fd
);
390 private bool is_atty (int fd
) {
391 Module module
= Module
.open (null, ModuleFlags
.BIND_LAZY
);
392 if (module
== null) {
397 module
.symbol ("isatty", out _func
);
402 AttyFunc? func
= (AttyFunc
) _func
;
403 return func (fd
) == 1;