diff options
author | zeertzjq <zeertzjq@outlook.com> | 2024-05-03 18:02:25 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-03 18:02:25 +0800 |
commit | d44ed3a885e163df33cce8180ca9f72fb5c0661a (patch) | |
tree | 48c5c793cc69a1bf35c712885bf123e7654e37d7 | |
parent | cf9f002f31c8b4d9d42912a3f45f5d3db4462fd9 (diff) | |
download | rneovim-d44ed3a885e163df33cce8180ca9f72fb5c0661a.tar.gz rneovim-d44ed3a885e163df33cce8180ca9f72fb5c0661a.tar.bz2 rneovim-d44ed3a885e163df33cce8180ca9f72fb5c0661a.zip |
perf(extmarks): better track whether namespace has extmarks (#28615)
This avoids redraw when adding/removing an empty namespace for a window.
This also avoids marktree traversal when clearing a namespace that has
already been cleared, which is added as a benchmark.
-rw-r--r-- | runtime/lua/vim/highlight.lua | 2 | ||||
-rw-r--r-- | src/nvim/api/extmark.c | 8 | ||||
-rw-r--r-- | src/nvim/extmark.c | 25 | ||||
-rw-r--r-- | src/nvim/memfile.c | 2 | ||||
-rw-r--r-- | test/benchmark/extmark_spec.lua | 45 | ||||
-rw-r--r-- | test/functional/api/extmark_spec.lua | 18 |
6 files changed, 89 insertions, 11 deletions
diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua index cdc0f0a0b1..781fabe5fb 100644 --- a/runtime/lua/vim/highlight.lua +++ b/runtime/lua/vim/highlight.lua @@ -129,13 +129,13 @@ function M.on_yank(opts) yank_cancel() end + vim.api.nvim_win_add_ns(winid, yank_ns) M.range(bufnr, yank_ns, higroup, "'[", "']", { regtype = event.regtype, inclusive = event.inclusive, priority = opts.priority or M.priorities.user, _scoped = true, }) - vim.api.nvim_win_add_ns(winid, yank_ns) yank_cancel = function() yank_timer = nil diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 60e12e9da8..320e14b654 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -1234,7 +1234,9 @@ Boolean nvim_win_add_ns(Window window, Integer ns_id, Error *err) set_put(uint32_t, &win->w_ns_set, (uint32_t)ns_id); - changed_window_setting(win); + if (map_has(uint32_t, win->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { + changed_window_setting(win); + } return true; } @@ -1279,7 +1281,9 @@ Boolean nvim_win_remove_ns(Window window, Integer ns_id, Error *err) set_del(uint32_t, &win->w_ns_set, (uint32_t)ns_id); - changed_window_setting(win); + if (map_has(uint32_t, win->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { + changed_window_setting(win); + } return true; } diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index c4a34f8019..3236590010 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -206,7 +206,9 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r } } - bool marks_cleared = false; + bool marks_cleared_any = false; + bool marks_cleared_all = l_row == 0 && l_col == 0; + MarkTreeIter itr[1] = { 0 }; marktree_itr_get(buf->b_marktree, l_row, l_col, itr); while (true) { @@ -214,16 +216,29 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r if (mark.pos.row < 0 || mark.pos.row > u_row || (mark.pos.row == u_row && mark.pos.col > u_col)) { + if (mark.pos.row >= 0) { + marks_cleared_all = false; + } break; } if (mark.ns == ns_id || all_ns) { - marks_cleared = true; + marks_cleared_any = true; extmark_del(buf, itr, mark, true); } else { marktree_itr_next(buf->b_marktree, itr); } } - return marks_cleared; + + if (marks_cleared_all) { + if (all_ns) { + map_destroy(uint32_t, buf->b_extmark_ns); + *buf->b_extmark_ns = (Map(uint32_t, uint32_t)) MAP_INIT; + } else { + map_del(uint32_t, uint32_t)(buf->b_extmark_ns, ns_id, NULL); + } + } + + return marks_cleared_any; } /// @return the position of marks between a range, @@ -315,10 +330,6 @@ MTPair extmark_from_id(buf_T *buf, uint32_t ns_id, uint32_t id) /// free extmarks from the buffer void extmark_free_all(buf_T *buf) { - if (!map_size(buf->b_extmark_ns)) { - return; - } - MarkTreeIter itr[1] = { 0 }; marktree_itr_get(buf->b_marktree, 0, 0, itr); while (true) { diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c index fb9f2eb8df..a1713edb66 100644 --- a/src/nvim/memfile.c +++ b/src/nvim/memfile.c @@ -677,7 +677,7 @@ static int mf_trans_add(memfile_T *mfp, bhdr_T *hp) /// The old number When not found. blocknr_T mf_trans_del(memfile_T *mfp, blocknr_T old_nr) { - blocknr_T *num = map_ref(int64_t, int64_t)(&mfp->mf_trans, old_nr, false); + blocknr_T *num = map_ref(int64_t, int64_t)(&mfp->mf_trans, old_nr, NULL); if (num == NULL) { // not found return old_nr; } diff --git a/test/benchmark/extmark_spec.lua b/test/benchmark/extmark_spec.lua new file mode 100644 index 0000000000..0d284b363c --- /dev/null +++ b/test/benchmark/extmark_spec.lua @@ -0,0 +1,45 @@ +local n = require('test.functional.testnvim')() + +local clear = n.clear +local exec_lua = n.exec_lua + +describe('extmark perf', function() + before_each(function() + clear() + + exec_lua([[ + out = {} + function start() + ts = vim.uv.hrtime() + end + function stop(name) + out[#out+1] = ('%14.6f ms - %s'):format((vim.uv.hrtime() - ts) / 1000000, name) + end + ]]) + end) + + after_each(function() + for _, line in ipairs(exec_lua([[return out]])) do + print(line) + end + end) + + it('repeatedly calling nvim_buf_clear_namespace #28615', function() + exec_lua([[ + vim.api.nvim_buf_set_lines(0, 0, -1, true, { 'foo', 'bar' }) + local ns0 = vim.api.nvim_create_namespace('ns0') + local ns1 = vim.api.nvim_create_namespace('ns1') + + for _ = 1, 10000 do + vim.api.nvim_buf_set_extmark(0, ns0, 0, 0, {}) + end + vim.api.nvim_buf_set_extmark(0, ns1, 1, 0, {}) + + start() + for _ = 1, 10000 do + vim.api.nvim_buf_clear_namespace(0, ns1, 0, -1) + end + stop('nvim_buf_clear_namespace') + ]]) + end) +end) diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index 70bdb050e5..965a60417a 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -1896,6 +1896,24 @@ describe('Extmarks buffer api with many marks', function() end eq(ns_marks[ns1], get_marks(ns1)) eq(ns_marks[ns2], get_marks(ns2)) + + api.nvim_buf_clear_namespace(0, ns1, 0, 10) + for id, mark in pairs(ns_marks[ns1]) do + if mark[1] < 10 then + ns_marks[ns1][id] = nil + end + end + eq(ns_marks[ns1], get_marks(ns1)) + eq(ns_marks[ns2], get_marks(ns2)) + + api.nvim_buf_clear_namespace(0, ns1, 20, -1) + for id, mark in pairs(ns_marks[ns1]) do + if mark[1] >= 20 then + ns_marks[ns1][id] = nil + end + end + eq(ns_marks[ns1], get_marks(ns1)) + eq(ns_marks[ns2], get_marks(ns2)) end) it('can delete line', function() |