gwion-util
utilities for the Gwion project
Loading...
Searching...
No Matches
prettyerr.c
Go to the documentation of this file.
1// libprettyerr: error.c
2// Copyright (C) 2021 Ethan Uppal
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17#include <string.h>
18#include <ctype.h>
19
20#include <termcolor.h>
21#include <prettyerr.h>
22
23// Used to declare isatty if on Unix
24#if defined(__unix__) || defined(__unix) || \
25 (defined(__APPLE__) && defined(__MACH__))
26#include <unistd.h>
27#endif
28
29#define _PRINTF(...) tcol_fprintf(printer->stream, __VA_ARGS__)
30#define _PUTCHR(...) fputc(__VA_ARGS__, printer->stream)
31
32static char const _tcol_lookup[5] = {'G', 'R', 'M', 'B', 'Y'};
33
34static char const *_ascii_box_lookup[8] = {"|", "|", "+", "-", "+", "|"};
35// static char const* _utf8_box_lookup[8] = { "┃", "╵", "┌", "─", "└", "│"};
36static char const *_utf8_box_lookup[8] = {"┃", "╵", "┌", "╭",
37 "─", "└", "╰", "│"};
38
39static const char *_errtype_lookup[5] = {"succes", "error", "warning", "info",
40 "hint"};
41
42void perr_printer_init(perr_printer_t *printer, FILE *stream,
43 const char *source, bool utf8, perr_runner_t style) {
44 printer->source = source;
45 printer->stream = stream;
46
47// Enables ANSI colors only in Unix terminals. False by default on Windows.
48#if defined(__unix__) || defined(__unix) || \
49 (defined(__APPLE__) && defined(__MACH__))
50 const int fd = fileno(stream);
51 // if (fd < 0) {
52 // perror("fileno");
53 // }
54 printer->color = isatty(fd);
55#else
56 printer->color = false;
57#endif
58
59 if ((printer->utf8 = utf8)) {
61 } else {
63 }
64 printer->runner = style;
65}
66
67static void perr_print_column(const perr_printer_t *printer, const char *color,
68 const size_t column) {
69 _PRINTF(" %s%s{0} %*s", color, printer->box_lookup[PERR_BOX_THICK_VERT],
70 (int)column, "");
71}
72
73static inline void _perr_print_filename(const perr_printer_t *printer,
74 const perr_t * err,
75 const size_t column) {
76 _PRINTF("{0}{+}%s{0}:%zu:%zu: ", err->filename, err->primary.line, column);
77}
78
79static void _perr_print_error(const perr_printer_t *printer, const perr_t *err,
80 const char *color) {
81 _PRINTF("%s%s", color, _errtype_lookup[err->type]);
82 if (err->error_code)
83 _PRINTF("[%c%04d]", toupper(_errtype_lookup[err->type][0]),
84 err->error_code);
85 _PRINTF("{0}: ");
86}
87
88// Print the line number and a | to denote the start of the line.
89void perr_print_line_number(const perr_printer_t *printer, const perr_t *err,
90 const char *color) {
91 _PRINTF("{-}%5zu{0} %s%s{0} ", err->primary.line, color,
93}
94
95// Print the line.
96static void _perr_print_offending_line(const perr_printer_t *printer,
97 const perr_t * err,
98 const char * error_line,
99 const char *color, const size_t column) {
100 _PRINTF("%.*s", (int)column, error_line);
101 const char *bug = error_line + column;
102 _PRINTF("%s{-}%.*s{0}", color, (int)err->error_position.length, bug);
103 const char *end = bug + err->error_position.length;
104 char * nl = strstr(end, "\n");
105 ptrdiff_t nl_index = nl ? nl - end : (ptrdiff_t)strlen(end) - 1;
106 _PRINTF("%.*s\n", (int)nl_index, end);
107}
108
109// Print a series of '^' showing where the error occurs.
110static inline void _perr_print_highlight_error(const perr_printer_t *printer,
111 const perr_t * err,
112 const char * error_line,
113 const char * color,
114 const size_t column,
115 const bool small) {
116 perr_print_column(printer, color, 0);
117 char* line = (char*)error_line;
118 size_t i = 0;
119 while(i < column) {
120 if(*line =='\t') _PRINTF("\t");
121 else _PRINTF(" ");
122 i++;
123 line++;
124 }
125 const enum libprettyerr_boxtype type =
126 (!small ? PERR_BOX_THIN_UL : PERR_BOX_THIN_BL) + printer->rounded;
127 if (err->error_position.length > 1) {
128 _PRINTF("%s{-}%s", color, printer->box_lookup[type]);
129 {
130 size_t i;
131 for (i = 1; i < err->error_position.length; i++) {
133 }
134 }
135 } else
136 _PRINTF("%s{-}%s", color, printer->box_lookup[PERR_BOX_THIN_VERT]);
137 _PRINTF("{0}\n");
138}
139
140// Print the fix, dimmed
141// TODO: this should be tertiary
142static inline void _perr_print_fix(const perr_printer_t *printer,
143 const char * fix) {
144 _PRINTF("{-}");
145 _PRINTF(fix);
146 _PRINTF("{0}\n");
147}
148
149static void lookup_color(char *color, enum libprettyerr_errtype type) {
150 char _color[16];
151 sprintf(_color, "+%c", _tcol_lookup[type]);
152 size_t len;
153 const int status = tcol_color_parse(color, 16, _color, 2, &len);
154 if (status != TermColorErrorNone) {
155 color[0] = 0;
156 // error
157 } else
158 color[len] = 0;
159}
160
161static inline void perr_print_basic_style(const perr_printer_t *printer,
162 const perr_t * err) {
163 // Normal errors:
164 //
165 // $filename:$line:$col: $type: $msg
166 // $line | <long line here>
167 // | ^^^^
168
169 // Here we scan backwards until we reach the start of the line (or the
170 // start of the source code). This allows us to retrieve the line that has
171 // the error.
172 size_t idx_cpy = err->error_position.index;
173 while (idx_cpy > 0 && printer->source[idx_cpy - 1] != '\n') { idx_cpy--; }
174 const char *error_line = (printer->source + idx_cpy);
175 error_line += (printer->source[idx_cpy] == '\n') ? 1 : 0;
176
177 // The column is how far the error's index is from the start-of-line's
178 // index.
179 const size_t column = err->error_position.index - idx_cpy;
180
181 char color[17];
182 lookup_color(color, err->type);
183
184 // Here we print the first row of the error message which provides general
185 // information such as filename, line, column, error type and a message.
186 _perr_print_filename(printer, err, column);
187 _perr_print_error(printer, err, color);
188 _PRINTF(err->main);
189 _PUTCHR('\n');
190
191 perr_print_line_number(printer, err, color);
192 _perr_print_offending_line(printer, err, error_line, color, column);
193 _perr_print_highlight_error(printer, err, error_line, color, column, !err->explain);
194
195 // Adds a subsidiary error note, if applicable
196 if (err->explain) {
197 perr_print_column(printer, color, column);
198 _PRINTF("{-}%s%s{0}\n", color, printer->box_lookup[PERR_BOX_THIN_HIGH]);
199 perr_print_column(printer, color, column);
200 _PRINTF(err->explain);
201 _PUTCHR('\n');
202 }
203}
204
205/*
206static inline void perr_print_complex(const perr_printer_t* printer,
207 const perr_t* err) {
208 // IDK COPY BRENDANZAB (DONT)
209}
210*/
211
212static inline void perr_print_secondary_style(const perr_printer_t *printer,
213 const perr_t * err) {
214 size_t idx_cpy = err->error_position.index;
215 while (idx_cpy > 0 && printer->source[idx_cpy - 1] != '\n') { idx_cpy--; }
216 const char *error_line = (printer->source + idx_cpy);
217 error_line += (printer->source[idx_cpy] == '\n') ? 1 : 0;
218
219 // The column is how far the error's index is from the start-of-line's
220 // index.
221 const size_t column = err->error_position.index - idx_cpy;
222
223 char color[17];
224 lookup_color(color, err->type);
225
226 _perr_print_filename(printer, err, column);
227 _PUTCHR('\n');
228
229 perr_print_line_number(printer, err, color);
230 _perr_print_offending_line(printer, err, error_line, color, column);
231 _perr_print_highlight_error(printer, err, error_line, color, column, false);
232
233 perr_print_column(printer, color, column);
234 _PRINTF("{-}%s%s%.*s{0} ", color,
235 printer->box_lookup[PERR_BOX_THIN_BL + printer->rounded], 3,
237 _PRINTF(err->main);
238 _PUTCHR('\n');
239}
240
241void perr_print_error(const perr_printer_t *printer, const perr_t *err) {
242 printer->runner(printer, err);
243}
244
static void _perr_print_highlight_error(const perr_printer_t *printer, const perr_t *err, const char *error_line, const char *color, const size_t column, const bool small)
Definition prettyerr.c:110
perr_runner_t perr_runner_secondary_style
Definition prettyerr.c:246
static void _perr_print_error(const perr_printer_t *printer, const perr_t *err, const char *color)
Definition prettyerr.c:79
static char const * _utf8_box_lookup[8]
Definition prettyerr.c:36
void perr_printer_init(perr_printer_t *printer, FILE *stream, const char *source, bool utf8, perr_runner_t style)
Definition prettyerr.c:42
perr_runner_t perr_runner_basic_style
Definition prettyerr.c:245
static void _perr_print_filename(const perr_printer_t *printer, const perr_t *err, const size_t column)
Definition prettyerr.c:73
static char const * _ascii_box_lookup[8]
Definition prettyerr.c:34
static void _perr_print_fix(const perr_printer_t *printer, const char *fix)
Definition prettyerr.c:142
static void _perr_print_offending_line(const perr_printer_t *printer, const perr_t *err, const char *error_line, const char *color, const size_t column)
Definition prettyerr.c:96
static void lookup_color(char *color, enum libprettyerr_errtype type)
Definition prettyerr.c:149
#define _PUTCHR(...)
Definition prettyerr.c:30
static char const _tcol_lookup[5]
Definition prettyerr.c:32
static const char * _errtype_lookup[5]
Definition prettyerr.c:39
void perr_print_line_number(const perr_printer_t *printer, const perr_t *err, const char *color)
Definition prettyerr.c:89
static void perr_print_secondary_style(const perr_printer_t *printer, const perr_t *err)
Definition prettyerr.c:212
#define _PRINTF(...)
Definition prettyerr.c:29
static void perr_print_column(const perr_printer_t *printer, const char *color, const size_t column)
Definition prettyerr.c:67
void perr_print_error(const perr_printer_t *printer, const perr_t *err)
Definition prettyerr.c:241
static void perr_print_basic_style(const perr_printer_t *printer, const perr_t *err)
Definition prettyerr.c:161
libprettyerr_boxtype
Definition prettyerr.h:50
@ PERR_BOX_THIN_VERT
Definition prettyerr.h:58
@ PERR_BOX_THICK_VERT
Definition prettyerr.h:51
@ PERR_BOX_THIN_BL
Definition prettyerr.h:56
@ PERR_BOX_THIN_HIGH
Definition prettyerr.h:52
@ PERR_BOX_THIN_UL
Definition prettyerr.h:53
@ PERR_BOX_THIN_HORIZ
Definition prettyerr.h:55
libprettyerr_errtype
Definition prettyerr.h:42
libprettyerr_runner_t perr_runner_t
Definition prettyerr.h:121
const char * filename
Definition prettyerr.h:72
const char * main
Definition prettyerr.h:68
struct libprettyerr_str primary
Definition prettyerr.h:65
const char * explain
Definition prettyerr.h:69
enum libprettyerr_errtype type
Definition prettyerr.h:63
struct libprettyerr_pos error_position
Definition prettyerr.h:66
const char ** box_lookup
Definition prettyerr.h:94
libprettyerr_runner_t runner
Definition prettyerr.h:95
const char * source
Definition prettyerr.h:92
int tcol_color_parse(char *dst, size_t dstn, char color[16], size_t k, size_t *len)
Definition termcolor.c:142
@ TermColorErrorNone
Definition termcolor.h:39