meta: add ivy-diag library

ivy-diag is used for generating and emitting diagnostic messages during compilation.
This commit is contained in:
2025-05-08 10:48:23 +01:00
parent 8e8637882d
commit c459f50e67
13 changed files with 1151 additions and 0 deletions

View File

@@ -30,6 +30,7 @@ endif ()
find_package(Bluelib REQUIRED) find_package(Bluelib REQUIRED)
add_subdirectory(common) add_subdirectory(common)
add_subdirectory(diag)
add_subdirectory(lang) add_subdirectory(lang)
add_subdirectory(asm) add_subdirectory(asm)
add_subdirectory(mie) add_subdirectory(mie)

15
diag/CMakeLists.txt Normal file
View File

@@ -0,0 +1,15 @@
file(GLOB_RECURSE diag_sources *.c *.h include/ivy/*.h)
if (WIN32)
set(rc_file ${CMAKE_CURRENT_SOURCE_DIR}/../res/win32/diag.rc)
endif ()
if (IVY_STATIC)
add_library(ivy-diag STATIC ${diag_sources} ${rc_file})
else ()
add_library(ivy-diag SHARED ${diag_sources} ${rc_file})
endif ()
target_include_directories(ivy-diag PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include/)
target_compile_definitions(ivy-diag PRIVATE IVY_EXPORT=1 IVY_STATIC=${IVY_STATIC})
target_link_libraries(ivy-diag ivy-common Bluelib::Core Bluelib::Object Bluelib::Term)

116
diag/ctx.c Normal file
View File

@@ -0,0 +1,116 @@
#include "ctx.h"
#include "diag.h"
#include "write.h"
#include <ivy/diag.h>
#include <stdlib.h>
#include <string.h>
enum ivy_status ivy_diag_ctx_create(struct ivy_diag_ctx **out)
{
struct ivy_diag_ctx *ctx = malloc(sizeof *ctx);
if (!ctx) {
return IVY_ERR_NO_MEMORY;
}
memset(ctx, 0x0, sizeof *ctx);
*out = ctx;
return IVY_OK;
}
void ivy_diag_ctx_destroy(struct ivy_diag_ctx *ctx)
{
}
enum ivy_status ivy_diag_set_line_source(
struct ivy_diag_ctx *ctx, struct ivy_line_source *src)
{
ctx->ctx_line_source = src;
return IVY_OK;
}
enum ivy_status ivy_diag_ctx_set_class_definitions(
struct ivy_diag_ctx *ctx, const struct ivy_diag_class *classes,
size_t nr_classes)
{
ctx->ctx_classes = classes;
ctx->ctx_nr_classes = nr_classes;
return IVY_OK;
}
enum ivy_status ivy_diag_ctx_set_msg_definitions(
struct ivy_diag_ctx *ctx, const struct ivy_diag_msg *msgs, size_t nr_msgs)
{
ctx->ctx_msg = msgs;
ctx->ctx_nr_msg = nr_msgs;
return IVY_OK;
}
void ivy_diag_ctx_write(
struct ivy_diag_ctx *ctx, enum ivy_diag_format format,
struct ivy_diag_stream *stream)
{
b_queue_iterator it;
b_queue_foreach (&it, &ctx->ctx_diags) {
struct ivy_diag *diag
= b_unbox(struct ivy_diag, it.entry, diag_entry);
diag_write(ctx, diag, format, stream);
}
}
struct ivy_diag *ivy_diag_ctx_create_diag(
struct ivy_diag_ctx *ctx, unsigned long diag_class)
{
struct ivy_diag *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->diag_class = diag_class;
out->diag_parent = ctx;
b_queue_push_back(&ctx->ctx_diags, &out->diag_entry);
return out;
}
const struct ivy_diag_class *diag_ctx_get_class(
struct ivy_diag_ctx *ctx, unsigned long class_id)
{
if (!ctx->ctx_classes) {
return NULL;
}
if (class_id >= ctx->ctx_nr_classes) {
return NULL;
}
const struct ivy_diag_class *out = &ctx->ctx_classes[class_id];
if (out->c_type == IVY_DIAG_NONE || !out->c_title) {
return NULL;
}
return out;
}
const struct ivy_diag_msg *diag_ctx_get_msg(
struct ivy_diag_ctx *ctx, unsigned long msg_id)
{
if (!ctx->ctx_msg) {
return NULL;
}
if (msg_id >= ctx->ctx_nr_msg) {
return NULL;
}
const struct ivy_diag_msg *out = &ctx->ctx_msg[msg_id];
if (!out->msg_content) {
return NULL;
}
return out;
}

26
diag/ctx.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef _DIAG_CTX_H_
#define _DIAG_CTX_H_
#include <blue/core/queue.h>
#include <ivy/diag.h>
struct ivy_line_source;
struct ivy_diag_ctx {
struct ivy_line_source *ctx_line_source;
const struct ivy_diag_class *ctx_classes;
size_t ctx_nr_classes;
const struct ivy_diag_msg *ctx_msg;
size_t ctx_nr_msg;
b_queue ctx_diags;
};
extern const struct ivy_diag_class *diag_ctx_get_class(
struct ivy_diag_ctx *ctx, unsigned long class_id);
extern const struct ivy_diag_msg *diag_ctx_get_msg(
struct ivy_diag_ctx *ctx, unsigned long msg_id);
#endif

96
diag/diag.c Normal file
View File

@@ -0,0 +1,96 @@
#include "diag.h"
#include "ctx.h"
#include <assert.h>
#include <blue/object/string.h>
#include <ivy/diag.h>
#include <stdlib.h>
#include <string.h>
struct diag_c_msg *diag_msg_create(const struct ivy_diag_msg *content)
{
struct diag_c_msg *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->msg_base.c_type = DIAG_COMPONENT_MSG;
out->msg_content = b_strdup(content->msg_content);
return out;
}
struct diag_c_snippet *diag_snippet_create(
unsigned long first_line, unsigned long last_line,
const struct ivy_diag_amendment *amendments, size_t nr_amendments,
const struct ivy_diag_highlight *highlights, size_t nr_highlights)
{
struct diag_c_snippet *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(out, 0x0, sizeof *out);
out->s_base.c_type = DIAG_COMPONENT_SNIPPET;
out->s_first_line = first_line;
out->s_last_line = last_line;
out->s_nr_amendments = nr_amendments;
out->s_amendments = calloc(nr_amendments, sizeof *amendments);
if (!out->s_amendments) {
free(out);
return NULL;
}
memcpy(out->s_amendments, amendments, nr_amendments * sizeof *amendments);
out->s_nr_highlights = nr_highlights;
out->s_highlights = calloc(nr_highlights, sizeof *highlights);
if (!out->s_highlights) {
free(out->s_amendments);
free(out);
return NULL;
}
memcpy(out->s_highlights, highlights, nr_highlights * sizeof *highlights);
return out;
}
void ivy_diag_set_location(
struct ivy_diag *diag, unsigned long row, unsigned long col)
{
diag->diag_row = row;
diag->diag_col = col;
}
void ivy_diag_push_msg(struct ivy_diag *diag, unsigned long msg, ...)
{
const struct ivy_diag_msg *msg_info
= diag_ctx_get_msg(diag->diag_parent, msg);
assert(msg_info);
struct diag_c_msg *c_msg = diag_msg_create(msg_info);
if (!c_msg) {
return;
}
b_queue_push_back(&diag->diag_components, &c_msg->msg_base.c_entry);
}
void ivy_diag_push_snippet(
struct ivy_diag *diag, unsigned long first_line, unsigned long last_line,
const struct ivy_diag_amendment *amendments, size_t nr_amendments,
const struct ivy_diag_highlight *highlights, size_t nr_highlights)
{
struct diag_c_snippet *c_snippet = diag_snippet_create(
first_line, last_line, amendments, nr_amendments, highlights,
nr_highlights);
if (!c_snippet) {
return;
}
b_queue_push_back(&diag->diag_components, &c_snippet->s_base.c_entry);
}

51
diag/diag.h Normal file
View File

@@ -0,0 +1,51 @@
#ifndef _DIAG_DIAG_H_
#define _DIAG_DIAG_H_
#include <blue/core/queue.h>
#include <stddef.h>
struct ivy_diag_msg;
enum diag_component_type {
DIAG_COMPONENT_NONE = 0,
DIAG_COMPONENT_MSG,
DIAG_COMPONENT_SNIPPET,
};
struct diag_component {
enum diag_component_type c_type;
b_queue_entry c_entry;
};
struct diag_c_msg {
struct diag_component msg_base;
char *msg_content;
};
struct diag_c_snippet {
struct diag_component s_base;
unsigned long s_first_line, s_last_line;
struct ivy_diag_amendment *s_amendments;
size_t s_nr_amendments;
struct ivy_diag_highlight *s_highlights;
size_t s_nr_highlights;
};
struct ivy_diag {
struct ivy_diag_ctx *diag_parent;
unsigned long diag_class;
unsigned long diag_row, diag_col;
b_queue_entry diag_entry;
b_queue diag_components;
};
extern struct diag_c_msg *diag_msg_create(const struct ivy_diag_msg *content);
extern struct diag_c_snippet *diag_snippet_create(
unsigned long first_line, unsigned long last_line,
const struct ivy_diag_amendment *amendmends, size_t nr_amendments,
const struct ivy_diag_highlight *highlights, size_t nr_highlights);
#endif

166
diag/include/ivy/diag.h Normal file
View File

@@ -0,0 +1,166 @@
#ifndef IVY_DIAG_H_
#define IVY_DIAG_H_
#include <ivy/status.h>
#include <stddef.h>
#include <stdio.h>
#define IVY_DIAG_HL(type, row0, col0, row1, col1) \
{ \
.h_type = IVY_DIAG_HIGHLIGHT_##type, .h_start_row = row0, \
.h_start_col = col0, .h_end_row = row1, .h_end_col = col1 \
}
#define IVY_DIAG_ADD(row, col, str) \
{ \
.a_type = IVY_DIAG_AMENDMENT_ADD, .__x = strlen(str), \
.a_add = { \
.a_row = (row), \
.a_col = (col), \
.a_str = (str), \
}, \
}
#define IVY_DIAG_REMOVE(row, col, len) \
{ \
.a_type = IVY_DIAG_AMENDMENT_REMOVE, \
.a_remove = { \
.a_row = (row), \
.a_col = (col), \
.a_length = (len), \
}, \
}
#define IVY_DIAG_REPLACE(row, col, len, str) \
{ \
.a_type = IVY_DIAG_AMENDMENT_REPLACE, .__x = strlen(str), \
.a_replace = { \
.a_row = (row), \
.a_col = (col), \
.a_length = (len), \
.a_str = (str), \
}, \
}
struct ivy_line_source;
struct ivy_diag_ctx;
struct ivy_diag;
struct b_tty;
enum ivy_diag_stream_flags {
IVY_DIAG_STREAM_F_NONE = 0x00u,
IVY_DIAG_STREAM_F_COLOUR = 0x01u,
};
enum ivy_diag_stream_type {
IVY_DIAG_STREAM_NONE = 0,
IVY_DIAG_STREAM_FILE,
IVY_DIAG_STREAM_TTY,
};
struct ivy_diag_stream {
enum ivy_diag_stream_type s_type;
enum ivy_diag_stream_flags s_flags;
size_t s_row, s_col;
int s_esc;
union {
struct b_tty *s_tty;
FILE *s_fp;
};
};
enum ivy_diag_type {
IVY_DIAG_NONE = 0,
IVY_DIAG_ERROR,
IVY_DIAG_WARNING,
IVY_DIAG_HINT,
};
enum ivy_diag_highlight_type {
IVY_DIAG_HIGHLIGHT_NONE = 0,
IVY_DIAG_HIGHLIGHT_ERROR,
IVY_DIAG_HIGHLIGHT_WARNING,
IVY_DIAG_HIGHLIGHT_HINT,
};
struct ivy_diag_highlight {
enum ivy_diag_highlight_type h_type;
unsigned long h_start_row, h_start_col;
unsigned long h_end_row, h_end_col;
};
enum ivy_diag_amendment_type {
IVY_DIAG_AMENDMENT_NONE = 0,
IVY_DIAG_AMENDMENT_ADD,
IVY_DIAG_AMENDMENT_REMOVE,
IVY_DIAG_AMENDMENT_REPLACE,
};
enum ivy_diag_format {
IVY_DIAG_FORMAT_NONE = 0,
IVY_DIAG_FORMAT_PRETTY,
IVY_DIAG_FORMAT_JSON,
};
struct ivy_diag_amendment {
enum ivy_diag_amendment_type a_type;
unsigned long __x;
union {
struct {
unsigned long a_row, a_col;
char *a_str;
} a_add;
struct {
unsigned long a_row, a_col;
unsigned long a_length;
} a_remove;
struct {
unsigned long a_row, a_col;
unsigned long a_length;
char *a_str;
} a_replace;
};
};
struct ivy_diag_class {
enum ivy_diag_type c_type;
const char *c_title;
};
struct ivy_diag_msg {
const char *msg_content;
};
extern void ivy_diag_stream_init_file(struct ivy_diag_stream *stream, FILE *fp);
extern void ivy_diag_stream_init_tty(
struct ivy_diag_stream *stream, struct b_tty *tty);
extern enum ivy_status ivy_diag_ctx_create(struct ivy_diag_ctx **out);
extern void ivy_diag_ctx_destroy(struct ivy_diag_ctx *ctx);
extern enum ivy_status ivy_diag_set_line_source(
struct ivy_diag_ctx *ctx, struct ivy_line_source *src);
extern enum ivy_status ivy_diag_ctx_set_class_definitions(
struct ivy_diag_ctx *ctx, const struct ivy_diag_class *classes,
size_t nr_classes);
extern enum ivy_status ivy_diag_ctx_set_msg_definitions(
struct ivy_diag_ctx *ctx, const struct ivy_diag_msg *msgs, size_t nr_msgs);
extern void ivy_diag_ctx_write(
struct ivy_diag_ctx *ctx, enum ivy_diag_format format,
struct ivy_diag_stream *stream);
extern struct ivy_diag *ivy_diag_ctx_create_diag(
struct ivy_diag_ctx *ctx, unsigned long diag_class);
extern void ivy_diag_set_location(
struct ivy_diag *diag, unsigned long row, unsigned long col);
extern void ivy_diag_push_msg(struct ivy_diag *diag, unsigned long msg, ...);
extern void ivy_diag_push_snippet(
struct ivy_diag *diag, unsigned long first_line, unsigned long last_line,
const struct ivy_diag_amendment *amendmends, size_t nr_amendments,
const struct ivy_diag_highlight *highlights, size_t nr_highlights);
#endif

134
diag/stream.c Normal file
View File

@@ -0,0 +1,134 @@
#include "stream.h"
#include <blue/term/tty.h>
#include <string.h>
void ivy_diag_stream_init_file(struct ivy_diag_stream *stream, FILE *fp)
{
memset(stream, 0x0, sizeof *stream);
stream->s_type = IVY_DIAG_STREAM_FILE;
stream->s_flags = IVY_DIAG_STREAM_F_NONE;
stream->s_fp = fp;
stream->s_row = 1;
stream->s_col = 1;
}
void ivy_diag_stream_init_tty(struct ivy_diag_stream *stream, struct b_tty *tty)
{
memset(stream, 0x0, sizeof *stream);
stream->s_type = IVY_DIAG_STREAM_TTY;
stream->s_flags = IVY_DIAG_STREAM_F_COLOUR;
stream->s_tty = tty;
stream->s_row = 1;
stream->s_col = 1;
}
enum ivy_status diag_stream_get_dimensions(
struct ivy_diag_stream *stream, size_t *out_rows, size_t *out_cols)
{
switch (stream->s_type) {
case IVY_DIAG_STREAM_FILE:
if (out_rows) {
*out_rows = (size_t)-1;
}
if (out_cols) {
*out_cols = (size_t)-1;
}
break;
case IVY_DIAG_STREAM_TTY: {
unsigned int w, h;
b_tty_get_dimensions(stream->s_tty, &w, &h);
if (out_rows) {
*out_rows = h;
}
if (out_cols) {
*out_cols = w;
}
break;
}
default:
return IVY_ERR_BAD_STATE;
}
return IVY_OK;
}
enum ivy_status diag_stream_putc(struct ivy_diag_stream *stream, char c)
{
enum ivy_status status = IVY_OK;
switch (stream->s_type) {
case IVY_DIAG_STREAM_FILE:
fputc(c, stream->s_fp);
status = (ferror(stream->s_fp)) ? IVY_ERR_IO_FAILURE : IVY_OK;
break;
case IVY_DIAG_STREAM_TTY:
b_tty_putc(stream->s_tty, 0, c);
status = IVY_OK;
break;
default:
status = IVY_ERR_BAD_STATE;
break;
}
if (stream->s_esc) {
if (c == '[' || c == ']') {
stream->s_esc = 0;
}
return status;
}
if (!stream->s_esc && c == '[') {
stream->s_esc = 1;
return status;
}
switch (c) {
case '\r':
stream->s_col = 1;
break;
case '\n':
stream->s_col = 1;
stream->s_row++;
break;
default:
stream->s_col++;
break;
}
return status;
}
enum ivy_status diag_stream_puts(struct ivy_diag_stream *stream, const char *s)
{
enum ivy_status status = IVY_OK;
while (*s && status == IVY_OK) {
status = diag_stream_putc(stream, *s);
s++;
}
return status;
}
enum ivy_status diag_stream_printf(
struct ivy_diag_stream *stream, const char *format, ...)
{
enum ivy_status status = IVY_OK;
va_list arg;
va_start(arg, format);
char buf[256];
vsnprintf(buf, sizeof buf, format, arg);
va_end(arg);
return diag_stream_puts(stream, buf);
}

18
diag/stream.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef _DIAG_STREAM_H_
#define _DIAG_STREAM_H_
#include <ivy/diag.h>
#define DIAG_STREAM_COL(sp) ((sp)->s_col)
#define DIAG_STREAM_ROW(sp) ((sp)->s_row)
#define DIAG_STREAM_FLAG_SET(sp, f) (((sp)->s_flags & (f)) != 0)
extern enum ivy_status diag_stream_get_dimensions(
struct ivy_diag_stream *stream, size_t *out_rows, size_t *out_cols);
extern enum ivy_status diag_stream_putc(struct ivy_diag_stream *stream, char c);
extern enum ivy_status diag_stream_puts(
struct ivy_diag_stream *stream, const char *s);
extern enum ivy_status diag_stream_printf(
struct ivy_diag_stream *stream, const char *format, ...);
#endif

26
diag/write.c Normal file
View File

@@ -0,0 +1,26 @@
#include "write.h"
#include <ivy/diag.h>
extern struct diag_writer pretty_writer;
static const struct diag_writer *writers[] = {
[IVY_DIAG_FORMAT_PRETTY] = &pretty_writer,
};
static const size_t nr_writers = sizeof writers / sizeof writers[0];
enum ivy_status diag_write(
struct ivy_diag_ctx *ctx, struct ivy_diag *diag,
enum ivy_diag_format format, struct ivy_diag_stream *stream)
{
if (format < 0 || format >= nr_writers) {
return IVY_ERR_INVALID_VALUE;
}
const struct diag_writer *writer = writers[format];
if (!writer) {
return IVY_ERR_INVALID_VALUE;
}
return writer->write(ctx, diag, stream);
}

15
diag/write.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef _DIAG_WRITE_H_
#define _DIAG_WRITE_H_
#include <ivy/diag.h>
struct diag_writer {
enum ivy_status (*write)(
struct ivy_diag_ctx *, struct ivy_diag *, struct ivy_diag_stream *);
};
extern enum ivy_status diag_write(
struct ivy_diag_ctx *ctx, struct ivy_diag *diag,
enum ivy_diag_format format, struct ivy_diag_stream *stream);
#endif

0
diag/write/json.c Normal file
View File

487
diag/write/pretty.c Normal file
View File

@@ -0,0 +1,487 @@
#include "../ctx.h"
#include "../diag.h"
#include "../stream.h"
#include "../write.h"
#include <assert.h>
#include <blue/core/stringstream.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;
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,
};