diff options
| author | bfredl <bjorn.linse@gmail.com> | 2025-09-09 20:53:31 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-09-09 20:53:31 +0200 |
| commit | b95aedeae4e57f3900410678f47e6ddd69a5357d (patch) | |
| tree | 758725fc1f91ff683ac68f67bce6b23ab4c17180 /src | |
| parent | d15936f6ef9b6d225b92d93369efaf2b1bb37474 (diff) | |
| parent | 5119c03be7a9d440ae1ab027c3879d2ad7e71bba (diff) | |
| download | rneovim-b95aedeae4e57f3900410678f47e6ddd69a5357d.tar.gz rneovim-b95aedeae4e57f3900410678f47e6ddd69a5357d.tar.bz2 rneovim-b95aedeae4e57f3900410678f47e6ddd69a5357d.zip | |
Merge pull request #35536 from bfredl/skipahead
perf(highlight): allow decoration providers to skip ranges without data
fixes #35644
Diffstat (limited to 'src')
| -rw-r--r-- | src/nvim/api/extmark.c | 19 | ||||
| -rw-r--r-- | src/nvim/api/keysets_defs.h | 2 | ||||
| -rw-r--r-- | src/nvim/decoration.c | 16 | ||||
| -rw-r--r-- | src/nvim/decoration.h | 2 | ||||
| -rw-r--r-- | src/nvim/decoration_defs.h | 9 | ||||
| -rw-r--r-- | src/nvim/decoration_provider.c | 76 | ||||
| -rw-r--r-- | src/nvim/lua/executor.c | 42 | ||||
| -rw-r--r-- | src/nvim/lua/executor.h | 5 |
8 files changed, 127 insertions, 44 deletions
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index c82e3840ca..a14cf426f4 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -836,6 +836,15 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col2 = c; } + DecorPriority subpriority = 0; + if (HAS_KEY(opts, set_extmark, _subpriority)) { + VALIDATE_RANGE((opts->_subpriority >= 0 && opts->_subpriority <= UINT16_MAX), + "_subpriority", { + goto error; + }); + subpriority = (DecorPriority)opts->_subpriority; + } + if (kv_size(virt_text.data.virt_text)) { decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_text, NULL), true); } @@ -845,7 +854,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (has_hl) { DecorSignHighlight sh = decor_sh_from_inline(hl); sh.url = url; - decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id); + decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id, + subpriority); } } else { if (opts->ephemeral) { @@ -1051,6 +1061,13 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// ``` /// ["range", winid, bufnr, begin_row, begin_col, end_row, end_col] /// ``` +/// +/// In addition to returning a boolean, it is also allowed to +/// return a `skip_row, skip_col` pair of integers. This implies +/// that this function does not need to be called until a range +/// which continues beyond the skipped position. A single integer +/// return value `skip_row` is short for `skip_row, 0` +/// /// - on_end: called at the end of a redraw cycle /// ``` /// ["end", tick] diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index e9a7637427..cb68d09186 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -62,6 +62,8 @@ typedef struct { Boolean undo_restore; String url; Boolean scoped; + + Integer _subpriority; } Dict(set_extmark); typedef struct { diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 7776adf185..2c64935b64 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -553,12 +553,12 @@ static void decor_range_add_from_inline(DecorState *state, int start_row, int st uint32_t idx = decor.data.ext.sh_idx; while (idx != DECOR_ID_INVALID) { DecorSignHighlight *sh = &kv_A(decor_items, idx); - decor_range_add_sh(state, start_row, start_col, end_row, end_col, sh, owned, ns, mark_id); + decor_range_add_sh(state, start_row, start_col, end_row, end_col, sh, owned, ns, mark_id, 0); idx = sh->next; } } else { DecorSignHighlight sh = decor_sh_from_inline(decor.data.hl); - decor_range_add_sh(state, start_row, start_col, end_row, end_col, &sh, owned, ns, mark_id); + decor_range_add_sh(state, start_row, start_col, end_row, end_col, &sh, owned, ns, mark_id, 0); } } @@ -619,14 +619,15 @@ void decor_range_add_virt(DecorState *state, int start_row, int start_col, int e .data.vt = vt, .attr_id = 0, .owned = owned, - .priority = vt->priority, + .priority_internal = ((DecorPriorityInternal)vt->priority << 16), .draw_col = -10, }; decor_range_insert(state, &range); } void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end_row, int end_col, - DecorSignHighlight *sh, bool owned, uint32_t ns, uint32_t mark_id) + DecorSignHighlight *sh, bool owned, uint32_t ns, uint32_t mark_id, + DecorPriority subpriority) { if (sh->flags & kSHIsSign) { return; @@ -638,7 +639,7 @@ void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end .data.sh = *sh, .attr_id = 0, .owned = owned, - .priority = sh->priority, + .priority_internal = ((DecorPriorityInternal)sh->priority << 16) + subpriority, .draw_col = -10, }; @@ -731,7 +732,7 @@ next_mark: break; } int const ordering = r->ordering; - DecorPriority const priority = r->priority; + DecorPriorityInternal const priority = r->priority_internal; int begin = 0; int end = cur_end; @@ -739,7 +740,8 @@ next_mark: int mid = begin + ((end - begin) >> 1); int mi = indices[mid]; DecorRange *mr = &slots[mi].range; - if (mr->priority < priority || (mr->priority == priority && mr->ordering < ordering)) { + if (mr->priority_internal < priority + || (mr->priority_internal == priority && mr->ordering < ordering)) { begin = mid + 1; } else { end = mid; diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index a1b0df21c2..08586eb238 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -37,7 +37,7 @@ typedef struct { int end_row; int end_col; int ordering; ///< range insertion order - DecorPriority priority; + DecorPriorityInternal priority_internal; bool owned; ///< ephemeral decoration, free memory immediately DecorRangeKind kind; // next pointers MUST NOT be used, these are separate ranges diff --git a/src/nvim/decoration_defs.h b/src/nvim/decoration_defs.h index 3f66d2cb61..c392880818 100644 --- a/src/nvim/decoration_defs.h +++ b/src/nvim/decoration_defs.h @@ -36,6 +36,7 @@ enum { typedef kvec_t(struct virt_line { VirtText line; int flags; }) VirtLines; typedef uint16_t DecorPriority; +typedef uint32_t DecorPriorityInternal; #define DECOR_PRIORITY_BASE 0x1000 /// Keep in sync with hl_mode_str[] in decoration.h @@ -145,6 +146,9 @@ typedef struct { kDecorProviderDisabled = 4, } state; + int win_skip_row; + int win_skip_col; + LuaRef redraw_start; LuaRef redraw_buf; LuaRef redraw_win; @@ -159,3 +163,8 @@ typedef struct { uint8_t error_count; } DecorProvider; + +#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \ + { ns_id, kDecorProviderDisabled, 0, 0, LUA_NOREF, LUA_NOREF, \ + LUA_NOREF, LUA_NOREF, LUA_NOREF, LUA_NOREF, \ + LUA_NOREF, LUA_NOREF, -1, false, false, 0 } diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c index ce6c2b8442..3019bd4df4 100644 --- a/src/nvim/decoration_provider.c +++ b/src/nvim/decoration_provider.c @@ -23,11 +23,6 @@ static kvec_t(DecorProvider) decor_providers = KV_INITIAL_VALUE; -#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \ - { ns_id, kDecorProviderDisabled, LUA_NOREF, LUA_NOREF, \ - LUA_NOREF, LUA_NOREF, LUA_NOREF, LUA_NOREF, \ - LUA_NOREF, LUA_NOREF, -1, false, false, 0 } - static void decor_provider_error(DecorProvider *provider, const char *name, const char *msg) { const char *ns = describe_ns(provider->ns_id, "(UNKNOWN PLUGIN)"); @@ -38,21 +33,28 @@ static void decor_provider_error(DecorProvider *provider, const char *name, cons // Note we pass in a provider index as this function may cause decor_providers providers to be // reallocated so we need to be careful with DecorProvider pointers static bool decor_provider_invoke(int provider_idx, const char *name, LuaRef ref, Array args, - bool default_true) + bool default_true, Array *res) { Error err = ERROR_INIT; textlock++; - Object ret = nlua_call_ref(ref, name, args, kRetNilBool, NULL, &err); + Object ret = nlua_call_ref(ref, name, args, res ? kRetMulti : kRetNilBool, NULL, &err); textlock--; // We get the provider here via an index in case the above call to nlua_call_ref causes // decor_providers to be reallocated. DecorProvider *provider = &kv_A(decor_providers, provider_idx); - if (!ERROR_SET(&err) - && api_object_to_bool(ret, "provider %s retval", default_true, &err)) { + if (!ERROR_SET(&err)) { provider->error_count = 0; - return true; + if (res) { + assert(ret.type == kObjectTypeArray); + *res = ret.data.array; + return true; + } else { + if (api_object_to_bool(ret, "provider %s retval", default_true, &err)) { + return true; + } + } } if (ERROR_SET(&err) && provider->error_count < CB_MAX_ERROR) { @@ -65,7 +67,7 @@ static bool decor_provider_invoke(int provider_idx, const char *name, LuaRef ref } api_clear_error(&err); - api_free_object(ret); + api_free_object(ret); // TODO(bfredl): wants to be on an arena return false; } @@ -81,7 +83,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((int)i, "spell", p->spell_nav, args, true); + decor_provider_invoke((int)i, "spell", p->spell_nav, args, true, NULL); } } } @@ -97,7 +99,7 @@ bool decor_providers_invoke_conceal_line(win_T *wp, int row) ADD_C(args, INTEGER_OBJ(wp->handle)); ADD_C(args, INTEGER_OBJ(wp->w_buffer->handle)); ADD_C(args, INTEGER_OBJ(row)); - decor_provider_invoke((int)i, "conceal_line", p->conceal_line, args, true); + decor_provider_invoke((int)i, "conceal_line", p->conceal_line, args, true, NULL); } } return wp->w_buffer->b_marktree->n_keys > keys; @@ -114,7 +116,7 @@ void decor_providers_start(void) if (p->state != kDecorProviderDisabled && p->redraw_start != LUA_NOREF) { MAXSIZE_TEMP_ARRAY(args, 2); ADD_C(args, INTEGER_OBJ((int)display_tick)); - bool active = decor_provider_invoke((int)i, "start", p->redraw_start, args, true); + bool active = decor_provider_invoke((int)i, "start", p->redraw_start, args, true, NULL); kv_A(decor_providers, i).state = active ? kDecorProviderActive : kDecorProviderRedrawDisabled; } else if (p->state != kDecorProviderDisabled) { kv_A(decor_providers, i).state = kDecorProviderActive; @@ -147,6 +149,9 @@ void decor_providers_invoke_win(win_T *wp) p->state = kDecorProviderActive; } + p->win_skip_row = 0; + p->win_skip_col = 0; + if (p->state == kDecorProviderActive && p->redraw_win != LUA_NOREF) { MAXSIZE_TEMP_ARRAY(args, 4); ADD_C(args, WINDOW_OBJ(wp->handle)); @@ -154,7 +159,8 @@ void decor_providers_invoke_win(win_T *wp) // 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(botline - 1)); - if (!decor_provider_invoke((int)i, "win", p->redraw_win, args, true)) { + // TODO(bfredl): could skip a call if retval was interpreted like range? + if (!decor_provider_invoke((int)i, "win", p->redraw_win, args, true, NULL)) { kv_A(decor_providers, i).state = kDecorProviderWinDisabled; } } @@ -178,7 +184,7 @@ void decor_providers_invoke_line(win_T *wp, 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((int)i, "line", p->redraw_line, args, true)) { + if (!decor_provider_invoke((int)i, "line", p->redraw_line, args, true, NULL)) { // return 'false' or error: skip rest of this window kv_A(decor_providers, i).state = kDecorProviderWinDisabled; } @@ -195,6 +201,10 @@ void decor_providers_invoke_range(win_T *wp, int start_row, int start_col, int e for (size_t i = 0; i < kv_size(decor_providers); i++) { DecorProvider *p = &kv_A(decor_providers, i); if (p->state == kDecorProviderActive && p->redraw_range != LUA_NOREF) { + if (p->win_skip_row > end_row || (p->win_skip_row == end_row && p->win_skip_col >= end_col)) { + continue; + } + MAXSIZE_TEMP_ARRAY(args, 6); ADD_C(args, WINDOW_OBJ(wp->handle)); ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle)); @@ -202,11 +212,35 @@ void decor_providers_invoke_range(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)); - if (!decor_provider_invoke((int)i, "range", p->redraw_range, args, true)) { - // return 'false' or error: skip rest of this window - kv_A(decor_providers, i).state = kDecorProviderWinDisabled; + Array res = ARRAY_DICT_INIT; + bool status = decor_provider_invoke((int)i, "range", p->redraw_range, args, true, &res); + p = &kv_A(decor_providers, i); // lua call might have reallocated decor_providers + + if (!status) { + // error: skip rest of this window + p->state = kDecorProviderWinDisabled; + } else if (res.size >= 1) { + Object first = res.items[0]; + if (first.type == kObjectTypeBoolean) { + if (first.data.boolean == false) { + p->state = kDecorProviderWinDisabled; + } + } else if (first.type == kObjectTypeInteger) { + Integer row = first.data.integer; + Integer col = 0; + if (res.size >= 2) { + Object second = res.items[1]; + if (second.type == kObjectTypeInteger) { + col = second.data.integer; + } + } + p->win_skip_row = (int)row; + p->win_skip_col = (int)col; + } } + api_free_array(res); + hl_check_ns(); } } @@ -226,7 +260,7 @@ void decor_providers_invoke_buf(buf_T *buf) MAXSIZE_TEMP_ARRAY(args, 2); ADD_C(args, BUFFER_OBJ(buf->handle)); ADD_C(args, INTEGER_OBJ((int64_t)display_tick)); - decor_provider_invoke((int)i, "buf", p->redraw_buf, args, true); + decor_provider_invoke((int)i, "buf", p->redraw_buf, args, true, NULL); } } } @@ -243,7 +277,7 @@ void decor_providers_invoke_end(void) if (p->state != kDecorProviderDisabled && p->redraw_end != LUA_NOREF) { MAXSIZE_TEMP_ARRAY(args, 1); ADD_C(args, INTEGER_OBJ((int)display_tick)); - decor_provider_invoke((int)i, "end", p->redraw_end, args, true); + decor_provider_invoke((int)i, "end", p->redraw_end, args, true, NULL); } } decor_check_to_be_deleted(); diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index e3b7abde46..1cedc73e92 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -181,12 +181,9 @@ int nlua_pcall(lua_State *lstate, int nargs, int nresults) lua_remove(lstate, -2); } else { if (nresults == LUA_MULTRET) { - int new_top = lua_gettop(lstate); - int actual_nres = new_top - pre_top + nargs + 1; - lua_remove(lstate, -1 - actual_nres); - } else { - lua_remove(lstate, -1 - nresults); + nresults = lua_gettop(lstate) - (pre_top - nargs - 1); } + lua_remove(lstate, -1 - nresults); } return status; } @@ -1551,6 +1548,7 @@ Object nlua_exec(const String str, const char *chunkname, const Array args, LuaR { lua_State *const lstate = global_lstate; + int top = lua_gettop(lstate); const char *name = (chunkname && chunkname[0]) ? chunkname : "<nvim>"; if (luaL_loadbuffer(lstate, str.data, str.size, name)) { size_t len; @@ -1570,7 +1568,7 @@ Object nlua_exec(const String str, const char *chunkname, const Array args, LuaR return NIL; } - return nlua_call_pop_retval(lstate, mode, arena, err); + return nlua_call_pop_retval(lstate, mode, arena, top, err); } bool nlua_ref_is_function(LuaRef ref) @@ -1601,10 +1599,16 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, LuaRetMode mode, return nlua_call_ref_ctx(false, ref, name, args, mode, arena, err); } +static int mode_ret(LuaRetMode mode) +{ + return mode == kRetMulti ? LUA_MULTRET : 1; +} + Object nlua_call_ref_ctx(bool fast, LuaRef ref, const char *name, Array args, LuaRetMode mode, Arena *arena, Error *err) { lua_State *const lstate = global_lstate; + int top = lua_gettop(lstate); nlua_pushref(lstate, ref); int nargs = (int)args.size; if (name != NULL) { @@ -1616,12 +1620,12 @@ Object nlua_call_ref_ctx(bool fast, LuaRef ref, const char *name, Array args, Lu } if (fast) { - if (nlua_fast_cfpcall(lstate, nargs, 1, -1) < 0) { + if (nlua_fast_cfpcall(lstate, nargs, mode_ret(mode), -1) < 0) { // error is already scheduled, set anyways to convey failure. api_set_error(err, kErrorTypeException, "fast context failure"); return NIL; } - } else if (nlua_pcall(lstate, nargs, 1)) { + } else if (nlua_pcall(lstate, nargs, mode_ret(mode))) { // if err is passed, the caller will deal with the error. if (err) { size_t len; @@ -1633,16 +1637,18 @@ Object nlua_call_ref_ctx(bool fast, LuaRef ref, const char *name, Array args, Lu return NIL; } - return nlua_call_pop_retval(lstate, mode, arena, err); + return nlua_call_pop_retval(lstate, mode, arena, top, err); } -static Object nlua_call_pop_retval(lua_State *lstate, LuaRetMode mode, Arena *arena, Error *err) +static Object nlua_call_pop_retval(lua_State *lstate, LuaRetMode mode, Arena *arena, int pretop, + Error *err) { - if (lua_isnil(lstate, -1)) { + if (mode != kRetMulti && lua_isnil(lstate, -1)) { lua_pop(lstate, 1); return NIL; } Error dummy = ERROR_INIT; + Error *perr = err ? err : &dummy; switch (mode) { case kRetNilBool: { @@ -1658,7 +1664,19 @@ static Object nlua_call_pop_retval(lua_State *lstate, LuaRetMode mode, Arena *ar return LUAREF_OBJ(ref); } case kRetObject: - return nlua_pop_Object(lstate, false, arena, err ? err : &dummy); + return nlua_pop_Object(lstate, false, arena, perr); + case kRetMulti: + ; + int nres = lua_gettop(lstate) - pretop; + Array res = arena_array(arena, (size_t)nres); + for (int i = 0; i < nres; i++) { + res.items[nres - i - 1] = nlua_pop_Object(lstate, false, arena, perr); + if (ERROR_SET(err)) { + return NIL; + } + } + res.size = (size_t)nres; + return ARRAY_OBJ(res); } UNREACHABLE; } diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index 9493015d10..c73b704a22 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -38,10 +38,11 @@ typedef struct { } while (0) typedef enum { - kRetObject, ///< any object, but doesn't preserve nested luarefs + kRetObject, ///< any object, but doesn't preserve nested luarefs kRetNilBool, ///< NIL preserved as such, other values return their booleanness ///< Should also be used when return value is ignored, as it is allocation-free - kRetLuaref, ///< return value becomes a single Luaref, regardless of type (except NIL) + kRetLuaref, ///< return value becomes a single Luaref, regardless of type (except NIL) + kRetMulti, ///< like kRetObject but return muliple return values as an Array } LuaRetMode; /// Maximum number of errors in vim.ui_attach() and decor provider callbacks. |