#include #include #include #include #include #include #include #include #include #include #include #include #define GET_CHAR_STOP 0 #define GET_CHAR_CONTINUE -1 struct snippet_print_ctx { b_tty *ctx_stream; struct mie_line_source *ctx_line_source; size_t ctx_row, ctx_col; bool ctx_has_underline; b_stringstream *ctx_underline; const b_string *ctx_linebuf; const b_iterator *ctx_linebuf_ptr; struct mie_diag_c_snippet *ctx_snippet; const struct mie_diag_highlight *ctx_hl; const struct mie_diag_amendment *ctx_amendment; }; #define __STREAM_COLOUR_ERROR "[red,bold]" #define __STREAM_COLOUR_WARN "[yellow,bold]" #define __STREAM_COLOUR_HINT "[blue,bold]" #define __STREAM_COLOUR_LINE_NO "[cyan]" #define __STREAM_COLOUR_ACCENT "[cyan]" #define __STREAM_COLOUR_RESET "[reset]" #define STREAM_COLOUR_ERROR(stream) \ b_tty_puts(stream, 0, __STREAM_COLOUR_ERROR); #define STREAM_COLOUR_WARN(stream) b_tty_puts(stream, 0, __STREAM_COLOUR_WARN); #define STREAM_COLOUR_HINT(stream) b_tty_puts(stream, 0, __STREAM_COLOUR_HINT); #define STREAM_COLOUR_ACCENT(stream) \ b_tty_puts(stream, 0, __STREAM_COLOUR_ACCENT); #define STREAM_COLOUR_LINE_NO(stream) \ b_tty_puts(stream, 0, __STREAM_COLOUR_LINE_NO); #define STREAM_COLOUR_RESET(stream) \ b_tty_puts(stream, 0, __STREAM_COLOUR_RESET); #define STREAM_COLOUR_ERROR_B(stream, str) \ b_stream_write_string(str, __STREAM_COLOUR_ERROR, NULL); #define STREAM_COLOUR_WARN_B(stream, str) \ b_stream_write_string(str, __STREAM_COLOUR_WARN, NULL); #define STREAM_COLOUR_HINT_B(stream, str) \ b_stream_write_string(str, __STREAM_COLOUR_HINT, NULL); #define STREAM_COLOUR_ACCENT_B(stream, str) \ b_stream_write_string(str, __STREAM_COLOUR_ACCENT, NULL); #define STREAM_COLOUR_RESET_B(stream, str) \ b_stream_write_string(str, __STREAM_COLOUR_RESET, NULL); static void print_header(struct mie_diag *diag, b_tty *dest) { unsigned int w; b_tty_get_dimensions(dest, &w, NULL); const struct mie_diag_class *diag_class = diag->diag_class; assert(diag_class); STREAM_COLOUR_ACCENT(dest); size_t nr_written = 0; nr_written += b_tty_puts(dest, 0, "--- "); const char *s = diag_class->c_title; while (*s) { b_tty_putc(dest, 0, toupper(*s)); s++; nr_written++; } b_tty_putc(dest, 0, ' '); nr_written++; while (nr_written < w) { b_tty_putc(dest, 0, '-'); nr_written++; } STREAM_COLOUR_RESET(dest); b_tty_putc(dest, 0, '\n'); } static void print_location(struct mie_diag *diag, b_tty *out) { size_t x; const char *name = mie_line_source_get_path(diag->diag_src); b_tty_puts(out, 0, " "); STREAM_COLOUR_ACCENT(out); b_tty_puts(out, 0, "> "); STREAM_COLOUR_RESET(out); b_tty_printf( out, 0, "%s:%lu:%lu\n", name, diag->diag_loc.c_row, diag->diag_loc.c_col); } static void print_msg( struct mie_diag *diag, struct mie_diag_c_msg *msg, b_tty *stream) { b_paragraph_format format = { .p_left_margin = 2, .p_right_margin = 2, }; b_print_paragraph(msg->msg_content, stream, &format); } static bool highlight_contains_cell( const struct mie_diag_highlight *hl, size_t row, size_t col) { if (row < hl->hl_span.s_start.c_row || row > hl->hl_span.s_end.c_row) { return false; } if (row == hl->hl_span.s_start.c_row && col < hl->hl_span.s_start.c_col) { return false; } if (row == hl->hl_span.s_end.c_row && col > hl->hl_span.s_end.c_col) { return false; } return true; } static bool amendment_contains_cell( const struct mie_diag_amendment *a, size_t row, size_t col) { unsigned long limit = 0; switch (a->a_type) { case MIE_DIAG_AMENDMENT_ADD: if (a->a_add.a_loc.c_row != row) { return false; } if (col < a->a_add.a_loc.c_col) { return false; } if (col > a->a_add.a_loc.c_col + a->__x - 1) { return false; } return true; case MIE_DIAG_AMENDMENT_REMOVE: if (a->a_remove.a_loc.c_row != row) { return false; } if (col < a->a_remove.a_loc.c_row) { return false; } if (col > a->a_remove.a_length) { return false; } return true; case MIE_DIAG_AMENDMENT_REPLACE: if (a->a_replace.a_loc.c_row != row) { return false; } if (col < a->a_replace.a_loc.c_row) { return false; } limit = a->a_replace.a_loc.c_row + b_max(ulong, a->a_replace.a_length, a->__x) - 1; if (col > limit) { return false; } return true; default: return false; } } static struct mie_diag_highlight *find_highlight( struct mie_diag_c_snippet *snippet, size_t row, size_t col) { for (size_t i = 0; i < snippet->s_nr_highlights; i++) { struct mie_diag_highlight *hl = &snippet->s_highlights[i]; if (highlight_contains_cell(hl, row, col)) { return hl; } } return NULL; } static struct mie_diag_amendment *find_amendment( struct mie_diag_c_snippet *snippet, size_t row, size_t col) { for (size_t i = 0; i < snippet->s_nr_amendments; i++) { struct mie_diag_amendment *a = &snippet->s_amendments[i]; if (amendment_contains_cell(a, row, col)) { return a; } } return NULL; } static void update_amendment(struct snippet_print_ctx *ctx) { if (ctx->ctx_amendment && amendment_contains_cell( ctx->ctx_amendment, ctx->ctx_row, ctx->ctx_col)) { return; } ctx->ctx_amendment = find_amendment(ctx->ctx_snippet, ctx->ctx_row, ctx->ctx_col); } static void update_highlighting(struct snippet_print_ctx *ctx) { if (ctx->ctx_hl && highlight_contains_cell(ctx->ctx_hl, ctx->ctx_row, ctx->ctx_col)) { return; } ctx->ctx_hl = NULL; STREAM_COLOUR_RESET(ctx->ctx_stream); const struct mie_diag_highlight *new_hl = find_highlight(ctx->ctx_snippet, ctx->ctx_row, ctx->ctx_col); if (!new_hl) { return; } ctx->ctx_has_underline = true; ctx->ctx_hl = new_hl; switch (ctx->ctx_hl->hl_type) { case MIE_DIAG_HIGHLIGHT_ERROR: STREAM_COLOUR_ERROR(ctx->ctx_stream); STREAM_COLOUR_ERROR_B(ctx->ctx_stream, ctx->ctx_underline); break; case MIE_DIAG_HIGHLIGHT_WARNING: STREAM_COLOUR_WARN(ctx->ctx_stream); STREAM_COLOUR_WARN_B(ctx->ctx_stream, ctx->ctx_underline); break; case MIE_DIAG_HIGHLIGHT_HINT: STREAM_COLOUR_HINT(ctx->ctx_stream); STREAM_COLOUR_HINT_B(ctx->ctx_stream, ctx->ctx_underline); break; default: break; } } static enum mie_status read_row(struct snippet_print_ctx *ctx, size_t row) { if (ctx->ctx_linebuf_ptr) { b_iterator_unref(ctx->ctx_linebuf_ptr); ctx->ctx_linebuf_ptr = NULL; } ctx->ctx_has_underline = false; size_t nr_read; enum mie_status status = mie_line_source_get_row( ctx->ctx_line_source, row, &ctx->ctx_linebuf); if (status != MIE_SUCCESS) { return status; } ctx->ctx_linebuf_ptr = b_iterator_cbegin(ctx->ctx_linebuf); return MIE_SUCCESS; } static int get_char_amendment(struct snippet_print_ctx *ctx) { const struct mie_diag_amendment *a = ctx->ctx_amendment; size_t i = 0; int c = 0; switch (a->a_type) { case MIE_DIAG_AMENDMENT_ADD: i = ctx->ctx_col - a->a_add.a_loc.c_row; return a->a_add.a_str[i]; case MIE_DIAG_AMENDMENT_REMOVE: b_iterator_move_next(ctx->ctx_linebuf_ptr); return GET_CHAR_CONTINUE; case MIE_DIAG_AMENDMENT_REPLACE: i = ctx->ctx_col - a->a_add.a_loc.c_col; if (i > a->__x) { i = a->__x; } c = a->a_replace.a_str[i]; if (i < a->a_replace.a_length) { b_iterator_move_next(ctx->ctx_linebuf_ptr); } if (c != 0) { return c; } return GET_CHAR_CONTINUE; default: break; } return GET_CHAR_CONTINUE; } static int get_char(struct snippet_print_ctx *ctx) { if (ctx->ctx_amendment) { return get_char_amendment(ctx); } b_wchar c = b_iterator_get_cvalue(ctx->ctx_linebuf_ptr).v_int; if (c == B_WCHAR_INVALID) { return GET_CHAR_STOP; }; b_iterator_move_next(ctx->ctx_linebuf_ptr); return c; } static void update_underline(struct snippet_print_ctx *ctx) { if (!ctx->ctx_hl) { b_stream_write_char(ctx->ctx_underline, ' '); return; } switch (ctx->ctx_hl->hl_type) { case MIE_DIAG_HIGHLIGHT_ERROR: case MIE_DIAG_HIGHLIGHT_WARNING: case MIE_DIAG_HIGHLIGHT_HINT: b_stream_write_char(ctx->ctx_underline, '^'); break; default: b_stream_write_char(ctx->ctx_underline, ' '); break; } } static void write_underline(struct snippet_print_ctx *ctx) { if (!ctx->ctx_has_underline) { return; } STREAM_COLOUR_RESET(ctx->ctx_stream); STREAM_COLOUR_LINE_NO(ctx->ctx_stream); b_tty_puts(ctx->ctx_stream, 0, " : "); b_tty_puts(ctx->ctx_stream, 0, b_stringstream_ptr(ctx->ctx_underline)); STREAM_COLOUR_RESET(ctx->ctx_stream); b_tty_putc(ctx->ctx_stream, 0, '\n'); } static void print_snippet( struct mie_diag *diag, struct mie_diag_c_snippet *snippet, b_tty *stream) { enum mie_status status = MIE_SUCCESS; struct snippet_print_ctx printer = { .ctx_stream = stream, .ctx_snippet = snippet, .ctx_underline = b_stringstream_create(), .ctx_line_source = diag->diag_src, }; for (unsigned long row = snippet->s_first_line; row <= snippet->s_last_line; row++) { STREAM_COLOUR_LINE_NO(stream); b_tty_printf(stream, 0, " %4lu | ", row); printer.ctx_row = row; status = read_row(&printer, row); if (status != MIE_SUCCESS) { break; } for (size_t i = 0;; i++) { printer.ctx_col = i + 1; update_amendment(&printer); int c = get_char(&printer); if (c == GET_CHAR_CONTINUE) { continue; } else if (c == GET_CHAR_STOP || c == '\n') { break; } update_highlighting(&printer); b_tty_putc(stream, 0, c); update_underline(&printer); } b_tty_putc(stream, 0, '\n'); write_underline(&printer); } b_stringstream_unref(printer.ctx_underline); } static void print_component( struct mie_diag *diag, struct mie_diag_component *component, b_tty *stream) { switch (component->c_type) { case MIE_DIAG_COMPONENT_MSG: print_msg(diag, (struct mie_diag_c_msg *)component, stream); break; case MIE_DIAG_COMPONENT_SNIPPET: print_snippet(diag, (struct mie_diag_c_snippet *)component, stream); break; default: abort(); } } enum mie_status mie_diag_write_pretty(struct mie_diag *diag, b_tty *stream) { print_header(diag, stream); print_location(diag, stream); b_queue_entry *entry = b_queue_first(&diag->diag_components); while (entry) { struct mie_diag_component *c = b_unbox(struct mie_diag_component, entry, c_entry); b_tty_putc(stream, 0, '\n'); print_component(diag, c, stream); entry = b_queue_next(entry); } return MIE_SUCCESS; }