diff options
-rw-r--r-- | src/nvim/decoration_provider.c | 52 | ||||
-rw-r--r-- | src/nvim/decoration_provider.h | 4 | ||||
-rw-r--r-- | src/nvim/drawline.c | 21 | ||||
-rw-r--r-- | src/nvim/drawscreen.c | 19 | ||||
-rw-r--r-- | src/nvim/message.c | 18 | ||||
-rw-r--r-- | src/nvim/spell.c | 9 | ||||
-rw-r--r-- | test/functional/ui/decorations_spec.lua | 22 |
7 files changed, 85 insertions, 60 deletions
diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c index 4bf61a92b0..5f2e2735a9 100644 --- a/src/nvim/decoration_provider.c +++ b/src/nvim/decoration_provider.c @@ -24,10 +24,17 @@ static kvec_t(DecorProvider) decor_providers = KV_INITIAL_VALUE; #define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \ { ns_id, false, 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, - bool default_true, char **perr) +static void decor_provider_error(DecorProvider *provider, const char *name, Error err) +{ + 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; @@ -39,17 +46,16 @@ static bool decor_provider_invoke(NS ns_id, const char *name, LuaRef ref, Array if (!ERROR_SET(&err) && api_object_to_bool(ret, "provider %s retval", default_true, &err)) { + provider->error_count = 0; return true; } if (ERROR_SET(&err)) { - const char *ns_name = describe_ns(ns_id); - ELOG("error in provider %s:%s: %s", ns_name, name, err.msg); - bool verbose_errs = true; // TODO(bfredl): - if (verbose_errs && perr && *perr == NULL) { - static char errbuf[IOSIZE]; - snprintf(errbuf, sizeof errbuf, "%s: %s", ns_name, err.msg); - *perr = xstrdup(errbuf); + decor_provider_error(provider, name, err); + provider->error_count++; + + if (provider->error_count >= DP_MAX_ERROR) { + provider->active = false; } } @@ -57,8 +63,7 @@ static bool decor_provider_invoke(NS ns_id, const char *name, LuaRef ref, Array return false; } -void decor_providers_invoke_spell(win_T *wp, int start_row, int start_col, int end_row, int end_col, - char **err) +void decor_providers_invoke_spell(win_T *wp, int start_row, int start_col, int end_row, int end_col) { for (size_t i = 0; i < kv_size(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(end_row)); 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] err Provider err -void decor_providers_start(DecorProviders *providers, char **err) +void decor_providers_start(DecorProviders *providers) { kvi_init(*providers); @@ -97,7 +102,7 @@ void decor_providers_start(DecorProviders *providers, char **err) if (p->redraw_start != LUA_NOREF) { MAXSIZE_TEMP_ARRAY(args, 2); 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 { 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] err Provider error void decor_providers_invoke_win(win_T *wp, DecorProviders *providers, - DecorProviders *line_providers, char **err) + DecorProviders *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? ADD_C(args, INTEGER_OBJ(wp->w_topline - 1)); 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); } } @@ -147,8 +152,7 @@ void decor_providers_invoke_win(win_T *wp, DecorProviders *providers, /// @param row Row to invoke line callback for /// @param[out] has_decor Set when at least one provider invokes a line callback /// @param[out] err Provider error -void decor_providers_invoke_line(win_T *wp, DecorProviders *providers, int row, bool *has_decor, - char **err) +void decor_providers_invoke_line(win_T *wp, DecorProviders *providers, int row, bool *has_decor) { decor_state.running_on_lines = true; 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, BUFFER_OBJ(wp->w_buffer->handle)); 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; } else { // 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 providers Decoration providers /// @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++) { 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); ADD_C(args, BUFFER_OBJ(buf->handle)); 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 displaytick Display tick /// @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++) { DecorProvider *p = kv_A(*providers, i); if (p && p->active && p->redraw_end != LUA_NOREF) { MAXSIZE_TEMP_ARRAY(args, 1); 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); } } } diff --git a/src/nvim/decoration_provider.h b/src/nvim/decoration_provider.h index b91ddabdfd..fa32eb97fe 100644 --- a/src/nvim/decoration_provider.h +++ b/src/nvim/decoration_provider.h @@ -8,6 +8,8 @@ #include "nvim/macros.h" #include "nvim/types.h" +#define DP_MAX_ERROR 3 + typedef struct { NS ns_id; bool active; @@ -20,6 +22,8 @@ typedef struct { LuaRef spell_nav; int hl_valid; bool hl_cached; + + uint8_t error_count; } DecorProvider; typedef kvec_withinit_t(DecorProvider *, 4) DecorProviders; diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 5aa9a76222..53e6b157c6 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -261,17 +261,6 @@ done: 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, int win_row) { @@ -1083,7 +1072,7 @@ static void win_line_continue(winlinevars_T *wlv) /// /// @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, - foldinfo_T foldinfo, DecorProviders *providers, char **provider_err) + foldinfo_T foldinfo, DecorProviders *providers) { 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); - decor_providers_invoke_line(wp, providers, lnum - 1, &has_decor, provider_err); - - if (*provider_err) { - provider_err_virt_text(lnum, *provider_err); - has_decor = true; - *provider_err = NULL; - } + decor_providers_invoke_line(wp, providers, lnum - 1, &has_decor); if (has_decor) { extra_check = true; diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 260f239ca8..1554a9304d 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -123,8 +123,6 @@ static bool redraw_popupmenu = false; static bool msg_grid_invalid = false; static bool resizing_autocmd = false; -static char *provider_err = NULL; - /// 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. @@ -542,7 +540,7 @@ int update_screen(void) ui_comp_set_screen_valid(true); DecorProviders providers; - decor_providers_start(&providers, &provider_err); + decor_providers_start(&providers); // "start" callback could have changed highlights for global elements if (win_check_ns_hl(NULL)) { @@ -589,7 +587,7 @@ int update_screen(void) } 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; } } @@ -669,7 +667,7 @@ int update_screen(void) } did_intro = true; - decor_providers_invoke_end(&providers, &provider_err); + decor_providers_invoke_end(&providers); kvi_destroy(providers); // 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); 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); @@ -2237,7 +2235,7 @@ static void win_update(win_T *wp, DecorProviders *providers) spellvars_T zero_spv = { 0 }; row = win_line(wp, lnum, srow, wp->w_grid.rows, false, foldinfo.fi_lines > 0 ? &zero_spv : &spv, - foldinfo, &line_providers, &provider_err); + foldinfo, &line_providers); if (foldinfo.fi_lines == 0) { 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. foldinfo_T info = wp->w_p_cul && lnum == wp->w_cursor.lnum ? cursorline_fi : fold_info(wp, lnum); - (void)win_line(wp, lnum, srow, wp->w_grid.rows, true, &spv, - info, &line_providers, &provider_err); + (void)win_line(wp, lnum, srow, wp->w_grid.rows, true, &spv, info, &line_providers); } // 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; wp->w_lines_valid = 0; wp->w_valid &= ~VALID_WCOL; - decor_providers_invoke_win(wp, providers, &line_providers, &provider_err); + decor_providers_invoke_win(wp, providers, &line_providers); continue; } @@ -2375,7 +2372,7 @@ static void win_update(win_T *wp, DecorProviders *providers) spellvars_T zero_spv = { 0 }; foldinfo_T zero_foldinfo = { 0 }; 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) { wp->w_botline = lnum; diff --git a/src/nvim/message.c b/src/nvim/message.c index c60e5c31fd..1f6790225c 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -885,6 +885,24 @@ void msg_schedule_semsg(const char *const fmt, ...) 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 /// "force" is true. This truncates in another way as for normal messages. /// Careful: The string may be changed by msg_may_trunc()! diff --git a/src/nvim/spell.c b/src/nvim/spell.c index c8a7bb2622..2ae0863a5c 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -1214,11 +1214,10 @@ static void decor_spell_nav_start(win_T *wp) decor_redraw_reset(wp, &decor_state); } -static TriState decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, int col, - char **decor_error) +static TriState decor_spell_nav_col(win_T *wp, linenr_T lnum, linenr_T *decor_lnum, int col) { 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_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; clearpos(&found_pos); - char *decor_error = NULL; // Ephemeral extmarks are currently stored in the global decor_state. // When looking for spell errors, we need to: // - 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 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: can_spell = true; break; case kFalse: @@ -1488,7 +1486,6 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att theend: decor_state_free(&decor_state); - xfree(decor_error); decor_state = saved_decor_start; xfree(buf); return ret; diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index cee1e32114..ef55784ca8 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -37,6 +37,7 @@ describe('decorations providers', function() [15] = {special = Screen.colors.Blue, undercurl = true}, [16] = {special = Screen.colors.Red, undercurl = true}, [17] = {foreground = Screen.colors.Red}, + [18] = {bold = true, foreground = Screen.colors.SeaGreen}; } end) @@ -611,6 +612,27 @@ describe('decorations providers', function() assert(eok == false) ]]) 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) local example_text = [[ |