#include "../ctx.h" #include "../diag.h" #include "../stream.h" #include "../write.h" #include #include #include #include #include #include #define GET_CHAR_STOP 0 #define GET_CHAR_CONTINUE -1 struct snippet_print_ctx { struct ivy_diag_stream *ctx_stream; struct ivy_line_source *ctx_line_source; size_t ctx_row, ctx_col; char ctx_underline_buf[256]; char ctx_line_buf[256]; size_t ctx_line_buf_ptr; bool ctx_has_underline; b_stringstream ctx_underline; struct diag_c_snippet *ctx_snippet; const struct ivy_diag_highlight *ctx_hl; const struct ivy_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_ACCENT "[cyan]" #define __STREAM_COLOUR_RESET "[reset]" #define STREAM_COLOUR_ERROR(stream) \ if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \ diag_stream_puts(stream, __STREAM_COLOUR_ERROR); \ } #define STREAM_COLOUR_WARN(stream) \ if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \ diag_stream_puts(stream, __STREAM_COLOUR_WARN); \ } #define STREAM_COLOUR_HINT(stream) \ if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \ diag_stream_puts(stream, __STREAM_COLOUR_HINT); \ } #define STREAM_COLOUR_ACCENT(stream) \ if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \ diag_stream_puts(stream, __STREAM_COLOUR_ACCENT); \ } #define STREAM_COLOUR_RESET(stream) \ if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \ diag_stream_puts(stream, __STREAM_COLOUR_RESET); \ } #define STREAM_COLOUR_ERROR_B(stream, str) \ if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \ b_stringstream_add(str, __STREAM_COLOUR_ERROR); \ } #define STREAM_COLOUR_WARN_B(stream, str) \ if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \ b_stringstream_add(str, __STREAM_COLOUR_WARN); \ } #define STREAM_COLOUR_HINT_B(stream, str) \ if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \ b_stringstream_add(str, __STREAM_COLOUR_HINT); \ } #define STREAM_COLOUR_ACCENT_B(stream, str) \ if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \ b_stringstream_add(str, __STREAM_COLOUR_ACCENT); \ } #define STREAM_COLOUR_RESET_B(stream, str) \ if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \ b_stringstream_add(str, __STREAM_COLOUR_RESET); \ } static void print_header( struct ivy_diag_ctx *ctx, struct ivy_diag *diag, struct ivy_diag_stream *stream) { size_t w; diag_stream_get_dimensions(stream, NULL, &w); const struct ivy_diag_class *diag_class = diag_ctx_get_class(ctx, diag->diag_class); assert(diag_class); STREAM_COLOUR_ACCENT(stream); diag_stream_puts(stream, "--- "); const char *s = diag_class->c_title; while (*s) { diag_stream_putc(stream, toupper(*s)); s++; } diag_stream_putc(stream, ' '); while (DIAG_STREAM_COL(stream) <= w) { diag_stream_putc(stream, '-'); } STREAM_COLOUR_RESET(stream); diag_stream_putc(stream, '\n'); } static void print_location( struct ivy_diag_ctx *ctx, struct ivy_diag *diag, struct ivy_diag_stream *stream) { char name[512]; size_t x; enum ivy_status status = ivy_line_source_get_name( ctx->ctx_line_source, name, sizeof name, &x); if (status != IVY_OK) { return; } diag_stream_puts(stream, " "); STREAM_COLOUR_ACCENT(stream); diag_stream_puts(stream, "> "); STREAM_COLOUR_RESET(stream); diag_stream_printf( stream, "%s:%lu:%lu\n", name, diag->diag_row, diag->diag_col); } static void print_msg( struct ivy_diag_ctx *ctx, struct ivy_diag *diag, struct diag_c_msg *msg, struct ivy_diag_stream *stream) { diag_stream_puts(stream, " "); diag_stream_puts(stream, msg->msg_content); diag_stream_putc(stream, '\n'); } static bool highlight_contains_cell( const struct ivy_diag_highlight *hl, size_t row, size_t col) { if (row < hl->h_start_row || row > hl->h_end_row) { return false; } if (row == hl->h_start_row && col < hl->h_start_col) { return false; } if (row == hl->h_end_row && col > hl->h_end_col) { return false; } return true; } static bool amendment_contains_cell( const struct ivy_diag_amendment *a, size_t row, size_t col) { unsigned long limit = 0; switch (a->a_type) { case IVY_DIAG_AMENDMENT_ADD: if (a->a_add.a_row != row) { return false; } if (col < a->a_add.a_col) { return false; } if (col > a->a_add.a_col + a->__x - 1) { return false; } return true; case IVY_DIAG_AMENDMENT_REMOVE: if (a->a_remove.a_row != row) { return false; } if (col < a->a_remove.a_col) { return false; } if (col > a->a_remove.a_length) { return false; } return true; case IVY_DIAG_AMENDMENT_REPLACE: if (a->a_replace.a_row != row) { return false; } if (col < a->a_replace.a_col) { return false; } limit = a->a_replace.a_col + b_max(ulong, a->a_replace.a_length, a->__x) - 1; if (col > limit) { return false; } return true; default: return false; } } static struct ivy_diag_highlight *find_highlight( struct diag_c_snippet *snippet, size_t row, size_t col) { b_queue_iterator it; for (size_t i = 0; i < snippet->s_nr_highlights; i++) { struct ivy_diag_highlight *hl = &snippet->s_highlights[i]; if (highlight_contains_cell(hl, row, col)) { return hl; } } return NULL; } static struct ivy_diag_amendment *find_amendment( struct diag_c_snippet *snippet, size_t row, size_t col) { b_queue_iterator it; for (size_t i = 0; i < snippet->s_nr_amendments; i++) { struct ivy_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 ivy_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->h_type) { case IVY_DIAG_HIGHLIGHT_ERROR: STREAM_COLOUR_ERROR(ctx->ctx_stream); STREAM_COLOUR_ERROR_B(ctx->ctx_stream, &ctx->ctx_underline); break; case IVY_DIAG_HIGHLIGHT_WARNING: STREAM_COLOUR_WARN(ctx->ctx_stream); STREAM_COLOUR_WARN_B(ctx->ctx_stream, &ctx->ctx_underline); break; case IVY_DIAG_HIGHLIGHT_HINT: STREAM_COLOUR_HINT(ctx->ctx_stream); STREAM_COLOUR_HINT_B(ctx->ctx_stream, &ctx->ctx_underline); break; default: break; } } static enum ivy_status read_row(struct snippet_print_ctx *ctx, size_t row) { ctx->ctx_line_buf[0] = 0; ctx->ctx_underline_buf[0] = 0; ctx->ctx_line_buf_ptr = 0; ctx->ctx_has_underline = false; b_stringstream_begin( &ctx->ctx_underline, ctx->ctx_underline_buf, sizeof ctx->ctx_underline_buf); size_t nr_read; return ivy_line_source_get_row( ctx->ctx_line_source, row, ctx->ctx_line_buf, sizeof ctx->ctx_line_buf, &nr_read); } static int get_char_amendment(struct snippet_print_ctx *ctx) { const struct ivy_diag_amendment *a = ctx->ctx_amendment; size_t i = 0; int c = 0; switch (a->a_type) { case IVY_DIAG_AMENDMENT_ADD: i = ctx->ctx_col - a->a_add.a_col; return a->a_add.a_str[i]; case IVY_DIAG_AMENDMENT_REMOVE: ctx->ctx_line_buf_ptr++; return GET_CHAR_CONTINUE; case IVY_DIAG_AMENDMENT_REPLACE: i = ctx->ctx_col - a->a_add.a_col; if (i > a->__x) { i = a->__x; } c = a->a_replace.a_str[i]; if (i < a->a_replace.a_length) { ctx->ctx_line_buf_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); } int c = ctx->ctx_line_buf[ctx->ctx_line_buf_ptr]; if (c == 0) { return GET_CHAR_STOP; }; ctx->ctx_line_buf_ptr++; return c; } static void update_underline(struct snippet_print_ctx *ctx) { if (!ctx->ctx_hl) { b_stringstream_add(&ctx->ctx_underline, " "); return; } switch (ctx->ctx_hl->h_type) { case IVY_DIAG_HIGHLIGHT_ERROR: case IVY_DIAG_HIGHLIGHT_WARNING: b_stringstream_add(&ctx->ctx_underline, "^"); break; case IVY_DIAG_HIGHLIGHT_HINT: b_stringstream_add(&ctx->ctx_underline, "-"); break; default: b_stringstream_add(&ctx->ctx_underline, " "); break; } } static void write_underline(struct snippet_print_ctx *ctx) { if (!ctx->ctx_has_underline) { return; } STREAM_COLOUR_ACCENT(ctx->ctx_stream); diag_stream_puts(ctx->ctx_stream, " | "); diag_stream_puts(ctx->ctx_stream, ctx->ctx_underline_buf); STREAM_COLOUR_RESET(ctx->ctx_stream); diag_stream_putc(ctx->ctx_stream, '\n'); } static void print_snippet( struct ivy_diag_ctx *ctx, struct ivy_diag *diag, struct diag_c_snippet *snippet, struct ivy_diag_stream *stream) { enum ivy_status status = IVY_OK; struct snippet_print_ctx printer = { .ctx_stream = stream, .ctx_snippet = snippet, .ctx_line_source = ctx->ctx_line_source, }; for (unsigned long row = snippet->s_first_line; row <= snippet->s_last_line; row++) { STREAM_COLOUR_ACCENT(stream); diag_stream_printf(stream, " %4lu | ", row); printer.ctx_row = row; status = read_row(&printer, row); if (status != IVY_OK) { 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) { break; } update_highlighting(&printer); diag_stream_putc(stream, c); update_underline(&printer); } diag_stream_putc(stream, '\n'); write_underline(&printer); } } static void print_component( struct ivy_diag_ctx *ctx, struct ivy_diag *diag, struct diag_component *component, struct ivy_diag_stream *stream) { switch (component->c_type) { case DIAG_COMPONENT_MSG: print_msg(ctx, diag, (struct diag_c_msg *)component, stream); break; case DIAG_COMPONENT_SNIPPET: print_snippet(ctx, diag, (struct diag_c_snippet *)component, stream); break; default: abort(); } } static enum ivy_status write( struct ivy_diag_ctx *ctx, struct ivy_diag *diag, struct ivy_diag_stream *stream) { print_header(ctx, diag, stream); print_location(ctx, diag, stream); b_queue_iterator it; b_queue_foreach (&it, &diag->diag_components) { struct diag_component *c = b_unbox(struct diag_component, it.entry, c_entry); diag_stream_putc(stream, '\n'); print_component(ctx, diag, c, stream); } return IVY_OK; } struct diag_writer pretty_writer = { .write = write, };