Files
ivy/diag/write/pretty.c

501 lines
12 KiB
C
Raw Normal View History

#include "../ctx.h"
#include "../diag.h"
#include "../stream.h"
#include "../write.h"
#include <assert.h>
#include <blue/core/stringstream.h>
2025-05-08 22:24:50 +01:00
#include <blue/term/print.h>
#include <blue/term/tty.h>
#include <ctype.h>
#include <ivy/line-source.h>
#include <stdlib.h>
#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;
2025-11-06 10:38:58 +00:00
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_LINE_NO "[cyan]"
#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_LINE_NO(stream) \
if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \
diag_stream_puts(stream, __STREAM_COLOUR_LINE_NO); \
}
#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)) { \
2025-11-06 10:38:58 +00:00
b_stream_write_string(str, __STREAM_COLOUR_ERROR, NULL); \
}
#define STREAM_COLOUR_WARN_B(stream, str) \
if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \
2025-11-06 10:38:58 +00:00
b_stream_write_string(str, __STREAM_COLOUR_WARN, NULL); \
}
#define STREAM_COLOUR_HINT_B(stream, str) \
if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \
2025-11-06 10:38:58 +00:00
b_stream_write_string(str, __STREAM_COLOUR_HINT, NULL); \
}
#define STREAM_COLOUR_ACCENT_B(stream, str) \
if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \
2025-11-06 10:38:58 +00:00
b_stream_write_string(str, __STREAM_COLOUR_ACCENT, NULL); \
}
#define STREAM_COLOUR_RESET_B(stream, str) \
if (DIAG_STREAM_FLAG_SET(stream, IVY_DIAG_STREAM_F_COLOUR)) { \
2025-11-06 10:38:58 +00:00
b_stream_write_string(str, __STREAM_COLOUR_RESET, NULL); \
}
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)
{
2025-05-08 22:24:50 +01:00
if (stream->s_type == IVY_DIAG_STREAM_TTY) {
b_paragraph_format format = {
.p_left_margin = 2,
.p_right_margin = 2,
};
b_print_paragraph(msg->msg_content, stream->s_tty, &format);
} else {
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)
{
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)
{
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);
2025-11-06 10:38:58 +00:00
STREAM_COLOUR_ERROR_B(ctx->ctx_stream, ctx->ctx_underline);
break;
case IVY_DIAG_HIGHLIGHT_WARNING:
STREAM_COLOUR_WARN(ctx->ctx_stream);
2025-11-06 10:38:58 +00:00
STREAM_COLOUR_WARN_B(ctx->ctx_stream, ctx->ctx_underline);
break;
case IVY_DIAG_HIGHLIGHT_HINT:
STREAM_COLOUR_HINT(ctx->ctx_stream);
2025-11-06 10:38:58 +00:00
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;
2025-11-06 10:38:58 +00:00
ctx->ctx_underline = b_stringstream_create_with_buffer(
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) {
2025-11-06 10:38:58 +00:00
b_stream_write_char(ctx->ctx_underline, ' ');
return;
}
switch (ctx->ctx_hl->h_type) {
case IVY_DIAG_HIGHLIGHT_ERROR:
case IVY_DIAG_HIGHLIGHT_WARNING:
case IVY_DIAG_HIGHLIGHT_HINT:
2025-11-06 10:38:58 +00:00
b_stream_write_char(ctx->ctx_underline, '^');
break;
default:
2025-11-06 10:38:58 +00:00
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);
2025-05-08 22:24:50 +01:00
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_LINE_NO(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);
2025-11-06 10:38:58 +00:00
b_queue_entry *entry = b_queue_first(&diag->diag_components);
while (entry) {
struct diag_component *c
2025-11-06 10:38:58 +00:00
= b_unbox(struct diag_component, entry, c_entry);
diag_stream_putc(stream, '\n');
print_component(ctx, diag, c, stream);
2025-11-06 10:38:58 +00:00
entry = b_queue_next(entry);
}
return IVY_OK;
}
struct diag_writer pretty_writer = {
.write = write,
};