From 4a706a70928231643e1a3f3fcf785de7aa930f8b Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Fri, 11 Apr 2025 13:46:55 +0200 Subject: [PATCH] fix(column): don't count signs on lines beyond eob #33410 Problem: Computed previous buffer line count may be beyond end of buffer. This results in signs being removed from `b_signcols` that were never included in it, tripping an assertion. Solution: Store the previous line count as it was before appending or deleting lines. Use it to clamp the edited region when clearing signs before a splice, after which it is reset. --- src/nvim/buffer_defs.h | 5 +++++ src/nvim/extmark.c | 5 +++-- src/nvim/memline.c | 6 ++++++ test/functional/ui/sign_spec.lua | 7 +++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 05d1829a31..1e2d8bbc93 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -720,6 +720,11 @@ struct file_buffer { MarkTree b_marktree[1]; Map(uint32_t, uint32_t) b_extmark_ns[1]; // extmark namespaces + // Store the line count as it was before appending or inserting lines. + // Used to determine a valid range before splicing marks, when the line + // count has already changed. + int b_prev_line_count; + // array of channel_id:s which have asked to receive updates for this // buffer. kvec_t(uint64_t) update_channels; diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index b981ffdaf9..051e47b0ac 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -580,8 +580,9 @@ void extmark_splice_impl(buf_T *buf, int start_row, colnr_T start_col, bcount_t // Remove signs inside edited region from "b_signcols.count", add after splicing. if (old_row > 0 || new_row > 0) { - int row2 = MIN(buf->b_ml.ml_line_count - (new_row - old_row) - 1, start_row + old_row); - buf_signcols_count_range(buf, start_row, row2, 0, kTrue); + int count = buf->b_prev_line_count > 0 ? buf->b_prev_line_count : buf->b_ml.ml_line_count; + buf_signcols_count_range(buf, start_row, MIN(count - 1, start_row + old_row), 0, kTrue); + buf->b_prev_line_count = 0; } marktree_splice(buf->b_marktree, (int32_t)start_row, start_col, diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 047bbbfdaa..058fdce99f 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -2088,6 +2088,9 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char *line, colnr_T len, boo dp = hp->bh_data; } + if (buf->b_prev_line_count == 0) { + buf->b_prev_line_count = buf->b_ml.ml_line_count; + } buf->b_ml.ml_line_count++; if ((int)dp->db_free >= space_needed) { // enough room in data block @@ -2573,6 +2576,9 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) int count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 2; int idx = lnum - buf->b_ml.ml_locked_low; + if (buf->b_prev_line_count == 0) { + buf->b_prev_line_count = buf->b_ml.ml_line_count; + } buf->b_ml.ml_line_count--; int line_start = ((dp->db_index[idx]) & DB_INDEX_MASK); diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua index 6ca9141a76..f72a781666 100644 --- a/test/functional/ui/sign_spec.lua +++ b/test/functional/ui/sign_spec.lua @@ -681,4 +681,11 @@ describe('Signs', function() :lua log, needs_clear = {}, true | ]]) end) + + it('signcolumn tracking does not consider signs beyond eob', function() + api.nvim_set_option_value('signcolumn', 'auto:2', {}) + api.nvim_buf_set_extmark(0, api.nvim_create_namespace(''), 1, 0, { sign_text = 's' }) + api.nvim_buf_set_lines(0, 0, -1, false, {}) + n.assert_alive() + end) end)