feat(decoration_provider): log errors as error messages

This commit is contained in:
Thomas Vigouroux 2023-07-19 17:56:25 +02:00 committed by GitHub
parent 2f22ed6a00
commit 30a5c28c87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 85 additions and 60 deletions

View File

@ -24,10 +24,17 @@ static kvec_t(DecorProvider) decor_providers = KV_INITIAL_VALUE;
#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \ #define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \
{ ns_id, false, LUA_NOREF, LUA_NOREF, \ { ns_id, false, LUA_NOREF, LUA_NOREF, \
LUA_NOREF, LUA_NOREF, LUA_NOREF, \ LUA_NOREF, LUA_NOREF, LUA_NOREF, \
LUA_NOREF, -1, false, false } LUA_NOREF, -1, false, false, 0 }
static bool decor_provider_invoke(NS ns_id, const char *name, LuaRef ref, Array args, static void decor_provider_error(DecorProvider *provider, const char *name, Error err)
bool default_true, char **perr) {
const char *ns_name = describe_ns(provider->ns_id);
ELOG("error in provider %s.%s: %s", ns_name, name, err.msg);
msg_schedule_semsg_multiline("Error in decoration provider %s.%s:\n%s", ns_name, name, err.msg);
}
static bool decor_provider_invoke(DecorProvider *provider, const char *name, LuaRef ref, Array args,
bool default_true)
{ {
Error err = ERROR_INIT; Error err = ERROR_INIT;
@ -39,17 +46,16 @@ static bool decor_provider_invoke(NS ns_id, const char *name, LuaRef ref, Array
if (!ERROR_SET(&err) if (!ERROR_SET(&err)
&& api_object_to_bool(ret, "provider %s retval", default_true, &err)) { && api_object_to_bool(ret, "provider %s retval", default_true, &err)) {
provider->error_count = 0;
return true; return true;
} }
if (ERROR_SET(&err)) { if (ERROR_SET(&err)) {
const char *ns_name = describe_ns(ns_id); decor_provider_error(provider, name, err);
ELOG("error in provider %s:%s: %s", ns_name, name, err.msg); provider->error_count++;
bool verbose_errs = true; // TODO(bfredl):
if (verbose_errs && perr && *perr == NULL) { if (provider->error_count >= DP_MAX_ERROR) {
static char errbuf[IOSIZE]; provider->active = false;
snprintf(errbuf, sizeof errbuf, "%s: %s", ns_name, err.msg);
*perr = xstrdup(errbuf);
} }
} }
@ -57,8 +63,7 @@ static bool decor_provider_invoke(NS ns_id, const char *name, LuaRef ref, Array
return false; return false;
} }
void decor_providers_invoke_spell(win_T *wp, int start_row, int start_col, int end_row, int end_col, void decor_providers_invoke_spell(win_T *wp, int start_row, int start_col, int end_row, int end_col)
char **err)
{ {
for (size_t i = 0; i < kv_size(decor_providers); i++) { for (size_t i = 0; i < kv_size(decor_providers); i++) {
DecorProvider *p = &kv_A(decor_providers, i); DecorProvider *p = &kv_A(decor_providers, i);
@ -74,7 +79,7 @@ void decor_providers_invoke_spell(win_T *wp, int start_row, int start_col, int e
ADD_C(args, INTEGER_OBJ(start_col)); ADD_C(args, INTEGER_OBJ(start_col));
ADD_C(args, INTEGER_OBJ(end_row)); ADD_C(args, INTEGER_OBJ(end_row));
ADD_C(args, INTEGER_OBJ(end_col)); ADD_C(args, INTEGER_OBJ(end_col));
decor_provider_invoke(p->ns_id, "spell", p->spell_nav, args, true, err); decor_provider_invoke(p, "spell", p->spell_nav, args, true);
} }
} }
} }
@ -83,7 +88,7 @@ void decor_providers_invoke_spell(win_T *wp, int start_row, int start_col, int e
/// ///
/// @param[out] providers Decoration providers /// @param[out] providers Decoration providers
/// @param[out] err Provider err /// @param[out] err Provider err
void decor_providers_start(DecorProviders *providers, char **err) void decor_providers_start(DecorProviders *providers)
{ {
kvi_init(*providers); kvi_init(*providers);
@ -97,7 +102,7 @@ void decor_providers_start(DecorProviders *providers, char **err)
if (p->redraw_start != LUA_NOREF) { if (p->redraw_start != LUA_NOREF) {
MAXSIZE_TEMP_ARRAY(args, 2); MAXSIZE_TEMP_ARRAY(args, 2);
ADD_C(args, INTEGER_OBJ((int)display_tick)); ADD_C(args, INTEGER_OBJ((int)display_tick));
active = decor_provider_invoke(p->ns_id, "start", p->redraw_start, args, true, err); active = decor_provider_invoke(p, "start", p->redraw_start, args, true);
} else { } else {
active = true; active = true;
} }
@ -116,7 +121,7 @@ void decor_providers_start(DecorProviders *providers, char **err)
/// @param[out] line_providers Enabled line providers to invoke in win_line /// @param[out] line_providers Enabled line providers to invoke in win_line
/// @param[out] err Provider error /// @param[out] err Provider error
void decor_providers_invoke_win(win_T *wp, DecorProviders *providers, void decor_providers_invoke_win(win_T *wp, DecorProviders *providers,
DecorProviders *line_providers, char **err) DecorProviders *line_providers)
{ {
kvi_init(*line_providers); kvi_init(*line_providers);
@ -133,7 +138,7 @@ void decor_providers_invoke_win(win_T *wp, DecorProviders *providers,
// TODO(bfredl): we are not using this, but should be first drawn line? // TODO(bfredl): we are not using this, but should be first drawn line?
ADD_C(args, INTEGER_OBJ(wp->w_topline - 1)); ADD_C(args, INTEGER_OBJ(wp->w_topline - 1));
ADD_C(args, INTEGER_OBJ(knownmax)); ADD_C(args, INTEGER_OBJ(knownmax));
if (decor_provider_invoke(p->ns_id, "win", p->redraw_win, args, true, err)) { if (decor_provider_invoke(p, "win", p->redraw_win, args, true)) {
kvi_push(*line_providers, p); kvi_push(*line_providers, p);
} }
} }
@ -147,8 +152,7 @@ void decor_providers_invoke_win(win_T *wp, DecorProviders *providers,
/// @param row Row to invoke line callback for /// @param row Row to invoke line callback for
/// @param[out] has_decor Set when at least one provider invokes a line callback /// @param[out] has_decor Set when at least one provider invokes a line callback
/// @param[out] err Provider error /// @param[out] err Provider error
void decor_providers_invoke_line(win_T *wp, DecorProviders *providers, int row, bool *has_decor, void decor_providers_invoke_line(win_T *wp, DecorProviders *providers, int row, bool *has_decor)
char **err)
{ {
decor_state.running_on_lines = true; decor_state.running_on_lines = true;
for (size_t k = 0; k < kv_size(*providers); k++) { for (size_t k = 0; k < kv_size(*providers); k++) {
@ -158,7 +162,7 @@ void decor_providers_invoke_line(win_T *wp, DecorProviders *providers, int row,
ADD_C(args, WINDOW_OBJ(wp->handle)); ADD_C(args, WINDOW_OBJ(wp->handle));
ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle)); ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle));
ADD_C(args, INTEGER_OBJ(row)); ADD_C(args, INTEGER_OBJ(row));
if (decor_provider_invoke(p->ns_id, "line", p->redraw_line, args, true, err)) { if (decor_provider_invoke(p, "line", p->redraw_line, args, true)) {
*has_decor = true; *has_decor = true;
} else { } else {
// return 'false' or error: skip rest of this window // return 'false' or error: skip rest of this window
@ -176,7 +180,7 @@ void decor_providers_invoke_line(win_T *wp, DecorProviders *providers, int row,
/// @param buf Buffer /// @param buf Buffer
/// @param providers Decoration providers /// @param providers Decoration providers
/// @param[out] err Provider error /// @param[out] err Provider error
void decor_providers_invoke_buf(buf_T *buf, DecorProviders *providers, char **err) void decor_providers_invoke_buf(buf_T *buf, DecorProviders *providers)
{ {
for (size_t i = 0; i < kv_size(*providers); i++) { for (size_t i = 0; i < kv_size(*providers); i++) {
DecorProvider *p = kv_A(*providers, i); DecorProvider *p = kv_A(*providers, i);
@ -184,7 +188,7 @@ void decor_providers_invoke_buf(buf_T *buf, DecorProviders *providers, char **er
MAXSIZE_TEMP_ARRAY(args, 2); MAXSIZE_TEMP_ARRAY(args, 2);
ADD_C(args, BUFFER_OBJ(buf->handle)); ADD_C(args, BUFFER_OBJ(buf->handle));
ADD_C(args, INTEGER_OBJ((int64_t)display_tick)); ADD_C(args, INTEGER_OBJ((int64_t)display_tick));
decor_provider_invoke(p->ns_id, "buf", p->redraw_buf, args, true, err); decor_provider_invoke(p, "buf", p->redraw_buf, args, true);
} }
} }
} }
@ -194,14 +198,14 @@ void decor_providers_invoke_buf(buf_T *buf, DecorProviders *providers, char **er
/// @param providers Decoration providers /// @param providers Decoration providers
/// @param displaytick Display tick /// @param displaytick Display tick
/// @param[out] err Provider error /// @param[out] err Provider error
void decor_providers_invoke_end(DecorProviders *providers, char **err) void decor_providers_invoke_end(DecorProviders *providers)
{ {
for (size_t i = 0; i < kv_size(*providers); i++) { for (size_t i = 0; i < kv_size(*providers); i++) {
DecorProvider *p = kv_A(*providers, i); DecorProvider *p = kv_A(*providers, i);
if (p && p->active && p->redraw_end != LUA_NOREF) { if (p && p->active && p->redraw_end != LUA_NOREF) {
MAXSIZE_TEMP_ARRAY(args, 1); MAXSIZE_TEMP_ARRAY(args, 1);
ADD_C(args, INTEGER_OBJ((int)display_tick)); ADD_C(args, INTEGER_OBJ((int)display_tick));
decor_provider_invoke(p->ns_id, "end", p->redraw_end, args, true, err); decor_provider_invoke(p, "end", p->redraw_end, args, true);
} }
} }
} }

View File

@ -8,6 +8,8 @@
#include "nvim/macros.h" #include "nvim/macros.h"
#include "nvim/types.h" #include "nvim/types.h"
#define DP_MAX_ERROR 3
typedef struct { typedef struct {
NS ns_id; NS ns_id;
bool active; bool active;
@ -20,6 +22,8 @@ typedef struct {
LuaRef spell_nav; LuaRef spell_nav;
int hl_valid; int hl_valid;
bool hl_cached; bool hl_cached;
uint8_t error_count;
} DecorProvider; } DecorProvider;
typedef kvec_withinit_t(DecorProvider *, 4) DecorProviders; typedef kvec_withinit_t(DecorProvider *, 4) DecorProviders;

View File

@ -261,17 +261,6 @@ done:
return cells; return cells;
} }
static inline void provider_err_virt_text(linenr_T lnum, char *err)
{
Decoration err_decor = DECORATION_INIT;
int hl_err = syn_check_group(S_LEN("ErrorMsg"));
kv_push(err_decor.virt_text,
((VirtTextChunk){ .text = err,
.hl_id = hl_err }));
err_decor.virt_text_width = (int)mb_string2cells(err);
decor_add_ephemeral(lnum - 1, 0, lnum - 1, 0, &err_decor, 0, 0);
}
static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int max_col, static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int max_col,
int win_row) int win_row)
{ {
@ -1083,7 +1072,7 @@ static void win_line_continue(winlinevars_T *wlv)
/// ///
/// @return the number of last row the line occupies. /// @return the number of last row the line occupies.
int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_only, spellvars_T *spv, int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_only, spellvars_T *spv,
foldinfo_T foldinfo, DecorProviders *providers, char **provider_err) foldinfo_T foldinfo, DecorProviders *providers)
{ {
winlinevars_T wlv; // variables passed between functions winlinevars_T wlv; // variables passed between functions
@ -1226,13 +1215,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
has_decor = decor_redraw_line(wp, lnum - 1, &decor_state); has_decor = decor_redraw_line(wp, lnum - 1, &decor_state);
decor_providers_invoke_line(wp, providers, lnum - 1, &has_decor, provider_err); decor_providers_invoke_line(wp, providers, lnum - 1, &has_decor);
if (*provider_err) {
provider_err_virt_text(lnum, *provider_err);
has_decor = true;
*provider_err = NULL;
}
if (has_decor) { if (has_decor) {
extra_check = true; extra_check = true;

View File

@ -123,8 +123,6 @@ static bool redraw_popupmenu = false;
static bool msg_grid_invalid = false; static bool msg_grid_invalid = false;
static bool resizing_autocmd = false; static bool resizing_autocmd = false;
static char *provider_err = NULL;
/// Check if the cursor line needs to be redrawn because of 'concealcursor'. /// Check if the cursor line needs to be redrawn because of 'concealcursor'.
/// ///
/// When cursor is moved at the same time, both lines will be redrawn regardless. /// When cursor is moved at the same time, both lines will be redrawn regardless.
@ -542,7 +540,7 @@ int update_screen(void)
ui_comp_set_screen_valid(true); ui_comp_set_screen_valid(true);
DecorProviders providers; DecorProviders providers;
decor_providers_start(&providers, &provider_err); decor_providers_start(&providers);
// "start" callback could have changed highlights for global elements // "start" callback could have changed highlights for global elements
if (win_check_ns_hl(NULL)) { if (win_check_ns_hl(NULL)) {
@ -589,7 +587,7 @@ int update_screen(void)
} }
if (buf->b_mod_tick_decor < display_tick) { if (buf->b_mod_tick_decor < display_tick) {
decor_providers_invoke_buf(buf, &providers, &provider_err); decor_providers_invoke_buf(buf, &providers);
buf->b_mod_tick_decor = display_tick; buf->b_mod_tick_decor = display_tick;
} }
} }
@ -669,7 +667,7 @@ int update_screen(void)
} }
did_intro = true; did_intro = true;
decor_providers_invoke_end(&providers, &provider_err); decor_providers_invoke_end(&providers);
kvi_destroy(providers); kvi_destroy(providers);
// either cmdline is cleared, not drawn or mode is last drawn // either cmdline is cleared, not drawn or mode is last drawn
@ -1446,7 +1444,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
decor_redraw_reset(wp, &decor_state); decor_redraw_reset(wp, &decor_state);
DecorProviders line_providers; DecorProviders line_providers;
decor_providers_invoke_win(wp, providers, &line_providers, &provider_err); decor_providers_invoke_win(wp, providers, &line_providers);
redraw_win_signcol(wp); redraw_win_signcol(wp);
@ -2237,7 +2235,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
spellvars_T zero_spv = { 0 }; spellvars_T zero_spv = { 0 };
row = win_line(wp, lnum, srow, wp->w_grid.rows, false, row = win_line(wp, lnum, srow, wp->w_grid.rows, false,
foldinfo.fi_lines > 0 ? &zero_spv : &spv, foldinfo.fi_lines > 0 ? &zero_spv : &spv,
foldinfo, &line_providers, &provider_err); foldinfo, &line_providers);
if (foldinfo.fi_lines == 0) { if (foldinfo.fi_lines == 0) {
wp->w_lines[idx].wl_folded = false; wp->w_lines[idx].wl_folded = false;
@ -2275,8 +2273,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
// text doesn't need to be drawn, but the number column does. // text doesn't need to be drawn, but the number column does.
foldinfo_T info = wp->w_p_cul && lnum == wp->w_cursor.lnum ? foldinfo_T info = wp->w_p_cul && lnum == wp->w_cursor.lnum ?
cursorline_fi : fold_info(wp, lnum); cursorline_fi : fold_info(wp, lnum);
(void)win_line(wp, lnum, srow, wp->w_grid.rows, true, &spv, (void)win_line(wp, lnum, srow, wp->w_grid.rows, true, &spv, info, &line_providers);
info, &line_providers, &provider_err);
} }
// This line does not need to be drawn, advance to the next one. // This line does not need to be drawn, advance to the next one.
@ -2297,7 +2294,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
lnum = wp->w_topline; lnum = wp->w_topline;
wp->w_lines_valid = 0; wp->w_lines_valid = 0;
wp->w_valid &= ~VALID_WCOL; wp->w_valid &= ~VALID_WCOL;
decor_providers_invoke_win(wp, providers, &line_providers, &provider_err); decor_providers_invoke_win(wp, providers, &line_providers);
continue; continue;
} }
@ -2375,7 +2372,7 @@ static void win_update(win_T *wp, DecorProviders *providers)
spellvars_T zero_spv = { 0 }; spellvars_T zero_spv = { 0 };
foldinfo_T zero_foldinfo = { 0 }; foldinfo_T zero_foldinfo = { 0 };
row = win_line(wp, wp->w_botline, row, wp->w_grid.rows, false, &zero_spv, row = win_line(wp, wp->w_botline, row, wp->w_grid.rows, false, &zero_spv,
zero_foldinfo, &line_providers, &provider_err); zero_foldinfo, &line_providers);
} }
} else if (dollar_vcol == -1) { } else if (dollar_vcol == -1) {
wp->w_botline = lnum; wp->w_botline = lnum;

View File

@ -885,6 +885,24 @@ void msg_schedule_semsg(const char *const fmt, ...)
loop_schedule_deferred(&main_loop, event_create(msg_semsg_event, 1, s)); loop_schedule_deferred(&main_loop, event_create(msg_semsg_event, 1, s));
} }
static void msg_semsg_multiline_event(void **argv)
{
char *s = argv[0];
(void)emsg_multiline(s, true);
xfree(s);
}
void msg_schedule_semsg_multiline(const char *const fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vim_vsnprintf((char *)IObuff, IOSIZE, fmt, ap);
va_end(ap);
char *s = xstrdup((char *)IObuff);
loop_schedule_deferred(&main_loop, event_create(msg_semsg_multiline_event, 1, s));
}
/// Like msg(), but truncate to a single line if p_shm contains 't', or when /// Like msg(), but truncate to a single line if p_shm contains 't', or when
/// "force" is true. This truncates in another way as for normal messages. /// "force" is true. This truncates in another way as for normal messages.
/// Careful: The string may be changed by msg_may_trunc()! /// Careful: The string may be changed by msg_may_trunc()!

View File

@ -1214,11 +1214,10 @@ static void decor_spell_nav_start(win_T *wp)
decor_redraw_reset(wp, &decor_state); decor_redraw_reset(wp, &decor_state);
} }
static TriState decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, int col, static TriState decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, int col)
char **decor_error)
{ {
if (*decor_lnum != lnum) { if (*decor_lnum != lnum) {
decor_providers_invoke_spell(wp, lnum - 1, col, lnum - 1, -1, decor_error); decor_providers_invoke_spell(wp, lnum - 1, col, lnum - 1, -1);
decor_redraw_line(wp, lnum - 1, &decor_state); decor_redraw_line(wp, lnum - 1, &decor_state);
*decor_lnum = lnum; *decor_lnum = lnum;
} }
@ -1277,7 +1276,6 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
linenr_T lnum = wp->w_cursor.lnum; linenr_T lnum = wp->w_cursor.lnum;
clearpos(&found_pos); clearpos(&found_pos);
char *decor_error = NULL;
// Ephemeral extmarks are currently stored in the global decor_state. // Ephemeral extmarks are currently stored in the global decor_state.
// When looking for spell errors, we need to: // When looking for spell errors, we need to:
// - temporarily reset decor_state // - temporarily reset decor_state
@ -1362,7 +1360,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
bool no_plain_buffer = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) != 0; bool no_plain_buffer = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) != 0;
bool can_spell = !no_plain_buffer; bool can_spell = !no_plain_buffer;
switch (decor_spell_nav_col(wp, lnum, &decor_lnum, col, &decor_error)) { switch (decor_spell_nav_col(wp, lnum, &decor_lnum, col)) {
case kTrue: case kTrue:
can_spell = true; break; can_spell = true; break;
case kFalse: case kFalse:
@ -1488,7 +1486,6 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
theend: theend:
decor_state_free(&decor_state); decor_state_free(&decor_state);
xfree(decor_error);
decor_state = saved_decor_start; decor_state = saved_decor_start;
xfree(buf); xfree(buf);
return ret; return ret;

View File

@ -37,6 +37,7 @@ describe('decorations providers', function()
[15] = {special = Screen.colors.Blue, undercurl = true}, [15] = {special = Screen.colors.Blue, undercurl = true},
[16] = {special = Screen.colors.Red, undercurl = true}, [16] = {special = Screen.colors.Red, undercurl = true},
[17] = {foreground = Screen.colors.Red}, [17] = {foreground = Screen.colors.Red},
[18] = {bold = true, foreground = Screen.colors.SeaGreen};
} }
end) end)
@ -611,6 +612,27 @@ describe('decorations providers', function()
assert(eok == false) assert(eok == false)
]]) ]])
end) end)
it('errors gracefully', function()
insert(mulholland)
setup_provider [[
function on_do(...)
error "Foo"
end
]]
screen:expect{grid=[[
{2:Error in decoration provider ns1.start:} |
{2:Error executing lua: [string "<nvim>"]:4}|
{2:: Foo} |
{2:stack traceback:} |
{2: [C]: in function 'error'} |
{2: [string "<nvim>"]:4: in function}|
{2: <[string "<nvim>"]:3>} |
{18:Press ENTER or type command to continue}^ |
]]}
end)
end) end)
local example_text = [[ local example_text = [[