tool: implement a diagnostic message printer
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
file(GLOB tool_sources
|
file(GLOB tool_sources
|
||||||
*.c *.h
|
*.c *.h
|
||||||
cmd/*.c cmd/*.h
|
cmd/*.c cmd/*.h
|
||||||
|
diag/*.c diag/*.h
|
||||||
cmd/internal/*.c)
|
cmd/internal/*.c)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
|||||||
11
tool/diag/diag.h
Normal file
11
tool/diag/diag.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef _TOOL_DIAG_DIAG_H_
|
||||||
|
#define _TOOL_DIAG_DIAG_H_
|
||||||
|
|
||||||
|
#include <blue/term/tty.h>
|
||||||
|
#include <mie/status.h>
|
||||||
|
|
||||||
|
struct mie_diag;
|
||||||
|
|
||||||
|
extern enum mie_status mie_diag_write_pretty(struct mie_diag *diag, b_tty *stream);
|
||||||
|
|
||||||
|
#endif
|
||||||
0
tool/diag/json.c
Normal file
0
tool/diag/json.c
Normal file
461
tool/diag/pretty.c
Normal file
461
tool/diag/pretty.c
Normal file
@@ -0,0 +1,461 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <blue/core/stringstream.h>
|
||||||
|
#include <blue/term/print.h>
|
||||||
|
#include <blue/term/tty.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <mie/diag/amendment.h>
|
||||||
|
#include <mie/diag/class.h>
|
||||||
|
#include <mie/diag/component.h>
|
||||||
|
#include <mie/diag/diag.h>
|
||||||
|
#include <mie/diag/highlight.h>
|
||||||
|
#include <mie/parse/line-source.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user