meta: add ivy-diag library
ivy-diag is used for generating and emitting diagnostic messages during compilation.
This commit is contained in:
@@ -30,6 +30,7 @@ endif ()
|
||||
find_package(Bluelib REQUIRED)
|
||||
|
||||
add_subdirectory(common)
|
||||
add_subdirectory(diag)
|
||||
add_subdirectory(lang)
|
||||
add_subdirectory(asm)
|
||||
add_subdirectory(mie)
|
||||
|
||||
15
diag/CMakeLists.txt
Normal file
15
diag/CMakeLists.txt
Normal 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
116
diag/ctx.c
Normal 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
26
diag/ctx.h
Normal 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
96
diag/diag.c
Normal 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
51
diag/diag.h
Normal 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
166
diag/include/ivy/diag.h
Normal 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
134
diag/stream.c
Normal 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
18
diag/stream.h
Normal 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
26
diag/write.c
Normal 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
15
diag/write.h
Normal 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
0
diag/write/json.c
Normal file
487
diag/write/pretty.c
Normal file
487
diag/write/pretty.c
Normal 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,
|
||||
};
|
||||
Reference in New Issue
Block a user