aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuuk van Baal <luukvbaal@gmail.com>2023-11-28 05:40:18 +0100
committerLewis Russell <me@lewisr.dev>2023-11-29 10:17:15 +0000
commitf4001d27efae44c6c07678ad2c72eed5f1a25ea8 (patch)
treecbcbeb8bdccd1d56860ef94ee30f669602624611
parent584c6c25ccfc5d13ffa0a4bd6efa467beb3987fe (diff)
downloadrneovim-f4001d27efae44c6c07678ad2c72eed5f1a25ea8.tar.gz
rneovim-f4001d27efae44c6c07678ad2c72eed5f1a25ea8.tar.bz2
rneovim-f4001d27efae44c6c07678ad2c72eed5f1a25ea8.zip
perf(column): only invalidate lines affected by added sign
-rw-r--r--src/nvim/buffer.c80
-rw-r--r--src/nvim/buffer_defs.h5
-rw-r--r--src/nvim/decoration.c24
-rw-r--r--src/nvim/drawscreen.c10
-rw-r--r--test/functional/ui/sign_spec.lua21
5 files changed, 64 insertions, 76 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 4d553cc91f..e6bedca232 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1844,7 +1844,7 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
buf = xcalloc(1, sizeof(buf_T));
// init b: variables
buf->b_vars = tv_dict_alloc();
- buf->b_signcols.valid = false;
+ buf->b_signcols.sentinel = 0;
init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE);
buf_init_changedtick(buf);
}
@@ -4026,88 +4026,64 @@ char *buf_spname(buf_T *buf)
return NULL;
}
-/// Invalidate the signcolumn if needed after deleting
-/// signs between line1 and line2 (inclusive).
-///
-/// @param buf buffer to check
-/// @param line1 start of region being deleted
-/// @param line2 end of region being deleted
+/// Invalidate the signcolumn if needed after deleting a sign ranging from line1 to line2.
void buf_signcols_del_check(buf_T *buf, linenr_T line1, linenr_T line2)
{
- if (!buf->b_signcols.valid) {
- return;
- }
-
- if (!buf->b_signcols.sentinel) {
- buf->b_signcols.valid = false;
- return;
- }
-
linenr_T sent = buf->b_signcols.sentinel;
-
if (sent >= line1 && sent <= line2) {
- // Only invalidate when removing signs at the sentinel line.
- buf->b_signcols.valid = false;
+ // When removed sign overlaps the sentinel line, entire buffer needs to be checked.
+ buf->b_signcols.sentinel = buf->b_signcols.size = 0;
}
}
-/// Re-calculate the signcolumn after adding a sign.
-///
-/// @param buf buffer to check
-/// @param added sign being added
+/// Invalidate the signcolumn if needed after adding a sign ranging from line1 to line2.
void buf_signcols_add_check(buf_T *buf, linenr_T line1, linenr_T line2)
{
- if (!buf->b_signcols.valid) {
- return;
- }
-
if (!buf->b_signcols.sentinel) {
- buf->b_signcols.valid = false;
return;
}
linenr_T sent = buf->b_signcols.sentinel;
-
if (sent >= line1 && sent <= line2) {
+ // If added sign overlaps sentinel line, increment without invalidating.
if (buf->b_signcols.size == buf->b_signcols.max) {
buf->b_signcols.max++;
}
buf->b_signcols.size++;
- redraw_buf_later(buf, UPD_NOT_VALID);
return;
}
- int signcols = decor_signcols(buf, line1 - 1, line2 - 1, SIGN_SHOW_MAX);
-
- if (signcols > buf->b_signcols.size) {
- buf->b_signcols.size = signcols;
- buf->b_signcols.max = signcols;
- redraw_buf_later(buf, UPD_NOT_VALID);
+ if (line1 < buf->b_signcols.invalid_top) {
+ buf->b_signcols.invalid_top = line1;
+ }
+ if (line2 > buf->b_signcols.invalid_bot) {
+ buf->b_signcols.invalid_bot = line2;
}
}
int buf_signcols(buf_T *buf, int max)
{
- // The maximum can be determined from 'signcolumn' which is window scoped so
- // need to invalidate signcols if the maximum is greater than the previous
- // (valid) maximum.
- if (buf->b_signcols.max && max > buf->b_signcols.max) {
- buf->b_signcols.valid = false;
- }
-
- if (!buf->b_signcols.valid) {
- buf->b_signcols.sentinel = 0;
- int signcols = decor_signcols(buf, 0, (int)buf->b_ml.ml_line_count - 1, max);
- // Check if we need to redraw
- if (signcols != buf->b_signcols.size) {
- buf->b_signcols.size = signcols;
- redraw_buf_later(buf, UPD_NOT_VALID);
+ if (!buf->b_signs_with_text) {
+ buf->b_signcols.size = 0;
+ } else if (max <= 1 && buf->b_signs_with_text >= (size_t)max) {
+ buf->b_signcols.size = max;
+ } else {
+ linenr_T sent = buf->b_signcols.sentinel;
+ if (!sent || max > buf->b_signcols.max) {
+ // Recheck if the window scoped maximum 'signcolumn' is greater than the
+ // previous maximum or if there is no sentinel line yet.
+ buf->b_signcols.invalid_top = sent ? sent : 1;
+ buf->b_signcols.invalid_bot = sent ? sent : buf->b_ml.ml_line_count;
}
- buf->b_signcols.max = max;
- buf->b_signcols.valid = true;
+ if (buf->b_signcols.invalid_bot) {
+ decor_validate_signcols(buf, max);
+ }
}
+ buf->b_signcols.max = max;
+ buf->b_signcols.invalid_top = MAXLNUM;
+ buf->b_signcols.invalid_bot = 0;
return buf->b_signcols.size;
}
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index e0119d2add..e59539f900 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -706,9 +706,10 @@ struct file_buffer {
struct {
int size; // last calculated number of sign columns
- bool valid; // calculated sign columns is valid
+ int max; // maximum value size is valid for.
linenr_T sentinel; // a line number which is holding up the signcolumn
- int max; // Maximum value size is valid for.
+ linenr_T invalid_top; // first invalid line number that needs to be checked
+ linenr_T invalid_bot; // last invalid line number that needs to be checked
} b_signcols;
Terminal *terminal; // Terminal instance associated with the buffer
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 407b54d133..11204a1b31 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -791,22 +791,15 @@ DecorSignHighlight *decor_find_sign(DecorInline decor)
}
}
-// Get the maximum required amount of sign columns needed between row and
-// end_row.
-int decor_signcols(buf_T *buf, int row, int end_row, int max)
+// Increase the signcolumn size and update the sentinel line if necessary for
+// the invalidated range.
+void decor_validate_signcols(buf_T *buf, int max)
{
- if (max <= 1 && buf->b_signs_with_text >= (size_t)max) {
- return max;
- }
-
- if (buf->b_signs_with_text == 0) {
- return 0;
- }
-
int signcols = 0; // highest value of count
+ int currow = buf->b_signcols.invalid_top - 1;
// TODO(bfredl): only need to use marktree_itr_get_overlap once.
// then we can process both start and end events and update state for each row
- for (int currow = row; currow <= end_row; currow++) {
+ for (; currow < buf->b_signcols.invalid_bot; currow++) {
MarkTreeIter itr[1];
if (!marktree_itr_get_overlap(buf->b_marktree, currow, 0, itr)) {
continue;
@@ -832,17 +825,16 @@ int decor_signcols(buf_T *buf, int row, int end_row, int max)
}
if (count > signcols) {
- if (count > buf->b_signcols.size) {
+ if (count >= buf->b_signcols.size) {
+ buf->b_signcols.size = count;
buf->b_signcols.sentinel = currow + 1;
}
if (count >= max) {
- return max;
+ return;
}
signcols = count;
}
}
-
- return signcols;
}
void decor_redraw_end(DecorState *state)
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index ea33c39178..6cc623cb72 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -606,10 +606,12 @@ int update_screen(void)
}
// Reset 'statuscolumn' if there is no dedicated signcolumn but it is invalid.
- if (*wp->w_p_stc != NUL && !wp->w_buffer->b_signcols.valid && wp->w_minscwidth <= SCL_NO) {
+ if (*wp->w_p_stc != NUL && wp->w_minscwidth <= SCL_NO
+ && (wp->w_buffer->b_signcols.invalid_bot || !wp->w_buffer->b_signcols.sentinel)) {
wp->w_nrwidth_line_count = 0;
wp->w_valid &= ~VALID_WCOL;
wp->w_redr_type = UPD_NOT_VALID;
+ wp->w_buffer->b_signcols.invalid_bot = 0;
}
}
@@ -618,11 +620,6 @@ int update_screen(void)
screen_search_hl.rm.regprog = NULL;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- // Validate b_signcols if there is no dedicated signcolumn but 'statuscolumn' is set.
- if (*wp->w_p_stc != NUL && wp->w_minscwidth <= SCL_NO) {
- buf_signcols(wp->w_buffer, 0);
- }
-
if (wp->w_redr_type == UPD_CLEAR && wp->w_floating && wp->w_grid_alloc.chars) {
grid_invalidate(&wp->w_grid_alloc);
wp->w_redr_type = UPD_NOT_VALID;
@@ -1213,6 +1210,7 @@ static void redraw_win_signcol(win_T *wp)
wp->w_scwidth = win_signcol_count(wp);
if (wp->w_scwidth != scwidth) {
changed_line_abv_curs_win(wp);
+ redraw_later(wp, UPD_NOT_VALID);
}
}
diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua
index adab184a4c..b12e79bd42 100644
--- a/test/functional/ui/sign_spec.lua
+++ b/test/functional/ui/sign_spec.lua
@@ -467,6 +467,27 @@ describe('Signs', function()
{0:~ }|
|
]])
+ -- should not increase size because sign with existing id is moved
+ command('sign place 4 line=1 name=pietSearch buffer=1')
+ screen:expect_unchanged()
+ command('sign unplace 4')
+ screen:expect([[
+ {1:>>>>>>}{6: 1 }a |
+ {2: }{6: 2 }b |
+ {2: }{6: 3 }c |
+ {2: }{6: 4 }^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ command('sign place 4 line=1 name=pietSearch buffer=1')
-- should keep the column at maximum size when signs are
-- exceeding the maximum
command('sign place 5 line=1 name=pietSearch buffer=1')