Release 0.41.92
[vala-gnome.git] / vala / valareport.vala
blob7667887062c16b3ddaf32fa8299364f709404d37
1 /* valareport.vala
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
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
23 using GLib;
26 /**
27 * Namespace to centralize reporting warnings and errors.
29 public class Vala.Report {
30 public enum Colored {
31 AUTO,
32 NEVER,
33 ALWAYS
36 /**
37 * SGR (Select Graphic Rendition) end tag
39 private const string ANSI_COLOR_END = "\x1b[0m";
41 /**
42 * SGR (Select Graphic Rendition) start tag for source location
44 private string locus_color_start = "";
46 /**
47 * SGR (Select Graphic Rendition) end tag for source location
49 private unowned string locus_color_end = "";
51 /**
52 * SGR (Select Graphic Rendition) start tag for warning titles
54 private string warning_color_start = "";
56 /**
57 * SGR (Select Graphic Rendition) end tag for warning titles
59 private unowned string warning_color_end = "";
61 /**
62 * SGR (Select Graphic Rendition) start tag for error titles
64 private string error_color_start = "";
66 /**
67 * SGR (Select Graphic Rendition) end tag for error titles
69 private unowned string error_color_end = "";
71 /**
72 * SGR (Select Graphic Rendition) start tag for note titles
74 private string note_color_start = "";
76 /**
77 * SGR (Select Graphic Rendition) end tag for note titles
79 private unowned string note_color_end = "";
81 /**
82 * SGR (Select Graphic Rendition) start tag for caret line (^^^)
84 private string caret_color_start = "";
86 /**
87 * SGR (Select Graphic Rendition) end tag for caret line (^^^)
89 private unowned string caret_color_end = "";
91 /**
92 * SGR (Select Graphic Rendition) start tag for quotes line ('...', `...`, `...')
94 private string quote_color_start = "";
96 /**
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
114 * {{{
115 * "error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01"
116 * }}}
118 public bool set_colors (string str, Report.Colored colored_output = Report.Colored.AUTO) {
119 try {
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) {
137 return false;
140 if (!val_regex.match (eq[1])) {
141 return false;
145 unowned string checked_value = eq[1]._strip ();
146 switch (eq[0]._strip ()) {
147 case "error":
148 error_color = checked_value;
149 break;
151 case "warning":
152 warning_color = checked_value;
153 break;
155 case "note":
156 note_color = checked_value;
157 break;
159 case "caret":
160 caret_color = checked_value;
161 break;
163 case "locus":
164 locus_color = checked_value;
165 break;
167 case "quote":
168 quote_color = checked_value;
169 break;
171 default:
172 return false;
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;
207 return true;
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 () {
221 return warnings;
225 * Returns the total number of errors reported.
227 public int get_errors () {
228 return 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
237 return;
240 string offending_line = source.file.get_source_line (source.begin.line);
242 if (offending_line != null) {
243 stderr.printf ("%s\n", offending_line);
244 int idx;
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");
252 } else {
253 stderr.printf (" ");
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");
261 } else {
262 stderr.printf ("^");
265 stderr.puts (caret_color_end);
267 stderr.printf ("\n");
271 private void print_highlighted_message (string message) {
272 int start = 0;
273 int cur = 0;
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));
279 start = cur;
280 cur++;
282 while (message[cur] != '\0' && end_chars.index_of_char (message[cur]) < 0) {
283 cur++;
285 if (message[cur] == '\0') {
286 stderr.puts (message.substring (start, cur - start));
287 start = cur;
288 } else {
289 cur++;
290 stderr.printf ("%s%s%s", quote_color_start, message.substring (start, cur - start), quote_color_end);
291 start = cur;
293 } else {
294 cur++;
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);
310 stderr.putc ('\n');
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) {
325 return;
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) {
339 return;
342 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) {
355 return;
358 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) {
370 errors++;
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) {
399 return false;
402 void* _func;
403 module.symbol ("isatty", out _func);
404 if (_func == null) {
405 return false;
408 AttyFunc? func = (AttyFunc) _func;
409 return func (fd) == 1;