aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/api/vim.c1
-rw-r--r--src/nvim/buffer.c66
-rw-r--r--src/nvim/buffer_defs.h9
-rw-r--r--src/nvim/decoration.c164
-rw-r--r--src/nvim/drawscreen.c54
-rw-r--r--src/nvim/extmark.c20
-rw-r--r--src/nvim/map.c3
-rw-r--r--src/nvim/map_defs.h3
-rw-r--r--src/nvim/move.c2
-rw-r--r--src/nvim/option.c14
-rw-r--r--src/nvim/optionstr.c2
-rw-r--r--src/nvim/textformat.c2
-rw-r--r--src/nvim/types_defs.h7
-rw-r--r--test/functional/ui/decorations_spec.lua21
14 files changed, 198 insertions, 170 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 0842469c59..2c937113e3 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -2189,7 +2189,6 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
int cul_id = 0;
int num_id = 0;
linenr_T lnum = statuscol_lnum;
- wp->w_scwidth = win_signcol_count(wp);
decor_redraw_signs(wp, wp->w_buffer, lnum - 1, sattrs, &line_id, &cul_id, &num_id);
statuscol.sattrs = sattrs;
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 8a594dea92..b5fac15af6 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -750,6 +750,8 @@ void buf_clear(void)
{
linenr_T line_count = curbuf->b_ml.ml_line_count;
extmark_free_all(curbuf); // delete any extmarks
+ map_destroy(int, curbuf->b_signcols.invalid);
+ *curbuf->b_signcols.invalid = (Map(int, SignRange)) MAP_INIT;
while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) {
ml_delete(1, false);
}
@@ -920,6 +922,8 @@ static void free_buffer_stuff(buf_T *buf, int free_flags)
}
uc_clear(&buf->b_ucmds); // clear local user commands
extmark_free_all(buf); // delete any extmarks
+ map_destroy(int, buf->b_signcols.invalid);
+ *buf->b_signcols.invalid = (Map(int, SignRange)) MAP_INIT;
map_clear_mode(buf, MAP_ALL_MODES, true, false); // clear local mappings
map_clear_mode(buf, MAP_ALL_MODES, true, true); // clear local abbrevs
XFREE_CLEAR(buf->b_start_fenc);
@@ -1844,7 +1848,6 @@ 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.sentinel = 0;
init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE);
buf_init_changedtick(buf);
}
@@ -4026,67 +4029,6 @@ char *buf_spname(buf_T *buf)
return NULL;
}
-/// 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)
-{
- linenr_T sent = buf->b_signcols.sentinel;
- if (sent >= line1 && sent <= line2) {
- // When removed sign overlaps the sentinel line, entire buffer needs to be checked.
- buf->b_signcols.sentinel = buf->b_signcols.size = 0;
- }
-}
-
-/// 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.sentinel) {
- 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++;
- return;
- }
-
- 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)
-{
- 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;
- }
-
- 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;
-}
-
/// Get "buf->b_fname", use "[No Name]" if it is NULL.
char *buf_get_fname(const buf_T *buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 7402e66403..beb3ec95b8 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -703,11 +703,10 @@ struct file_buffer {
// may use a different synblock_T.
struct {
- int size; // last calculated number of sign columns
- int max; // maximum value size is valid for.
- linenr_T sentinel; // a line number which is holding up the signcolumn
- 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
+ int max; // maximum number of signs on a single line
+ int max_count; // number of lines with max number of signs
+ bool resized; // whether max changed at start of redraw
+ Map(int, SignRange) invalid[1]; // map of invalid ranges 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 ccba8bd607..20311d80e5 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -203,21 +203,21 @@ void buf_put_decor_virt(buf_T *buf, DecorVirtText *vt)
}
static int sign_add_id = 0;
-void buf_put_decor_sh(buf_T *buf, DecorSignHighlight *sh, int row, int row2)
+void buf_put_decor_sh(buf_T *buf, DecorSignHighlight *sh, int row1, int row2)
{
if (sh->flags & kSHIsSign) {
sh->sign_add_id = sign_add_id++;
buf->b_signs++;
if (sh->text.ptr) {
buf->b_signs_with_text++;
- buf_signcols_add_check(buf, row + 1, row2 + 1);
+ buf_signcols_invalidate_range(buf, row1, row2, 1);
}
}
}
-void buf_decor_remove(buf_T *buf, int row, int row2, DecorInline decor, bool free)
+void buf_decor_remove(buf_T *buf, int row1, int row2, DecorInline decor, bool free)
{
- decor_redraw(buf, row, row2, decor);
+ decor_redraw(buf, row1, row2, decor);
if (decor.ext) {
DecorVirtText *vt = decor.data.ext.vt;
while (vt) {
@@ -227,7 +227,7 @@ void buf_decor_remove(buf_T *buf, int row, int row2, DecorInline decor, bool fre
uint32_t idx = decor.data.ext.sh_idx;
while (idx != DECOR_ID_INVALID) {
DecorSignHighlight *sh = &kv_A(decor_items, idx);
- buf_remove_decor_sh(buf, row, row2, sh);
+ buf_remove_decor_sh(buf, row1, row2, sh);
idx = sh->next;
}
if (free) {
@@ -249,7 +249,7 @@ void buf_remove_decor_virt(buf_T *buf, DecorVirtText *vt)
}
}
-void buf_remove_decor_sh(buf_T *buf, int row, int row2, DecorSignHighlight *sh)
+void buf_remove_decor_sh(buf_T *buf, int row1, int row2, DecorSignHighlight *sh)
{
if (sh->flags & kSHIsSign) {
assert(buf->b_signs > 0);
@@ -257,8 +257,8 @@ void buf_remove_decor_sh(buf_T *buf, int row, int row2, DecorSignHighlight *sh)
if (sh->text.ptr) {
assert(buf->b_signs_with_text > 0);
buf->b_signs_with_text--;
- if (row2 >= row) {
- buf_signcols_del_check(buf, row + 1, row2 + 1);
+ if (row2 >= row1) {
+ buf_signcols_invalidate_range(buf, row1, row2, -1);
}
}
}
@@ -792,50 +792,128 @@ DecorSignHighlight *decor_find_sign(DecorInline decor)
}
}
-// 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 "count" is greater than current max, set it and reset "max_count".
+static void buf_signcols_validate_row(buf_T *buf, int count, int add)
{
- 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 (; currow < buf->b_signcols.invalid_bot; currow++) {
- MarkTreeIter itr[1];
- if (!marktree_itr_get_overlap(buf->b_marktree, currow, 0, itr)) {
- continue;
- }
+ int del = add < 0 ? -add : 0;
+ if (count > buf->b_signcols.max) {
+ buf->b_signcols.max = count;
+ buf->b_signcols.max_count = 0;
+ buf->b_signcols.resized = true;
+ }
+ /// Add sign of "add" to "max_count"
+ if (count == buf->b_signcols.max - del) {
+ buf->b_signcols.max_count += (add > 0) - (add < 0);
+ }
+}
- int count = 0;
- MTPair pair;
- while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) {
- if (!mt_invalid(pair.start) && (pair.start.flags & MT_FLAG_DECOR_SIGNTEXT)) {
- count++;
- }
+/// Validate a range by counting the number of overlapping signs and adjusting
+/// "b_signcols" accordingly.
+static void buf_signcols_validate_range(buf_T *buf, int row1, int row2, int add)
+{
+ int count = 0; // Number of signs on the current line
+ int currow = row1;
+ MTPair pair = { 0 };
+ MarkTreeIter itr[1];
+
+ // Allocate an array of integers holding the overlapping signs in the range.
+ assert(row2 >= row1);
+ int *overlap = xcalloc(sizeof(int), (size_t)(row2 + 1 - row1));
+
+ // First find the number of overlapping signs at "row1".
+ marktree_itr_get_overlap(buf->b_marktree, currow, 0, itr);
+ while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) {
+ if (!mt_invalid(pair.start) && pair.start.flags & MT_FLAG_DECOR_SIGNTEXT) {
+ overlap[0]++;
}
+ }
- while (itr->x) {
- MTKey mark = marktree_itr_current(itr);
- if (mark.pos.row != currow) {
- break;
- }
- if (!mt_invalid(mark) && !mt_end(mark) && (mark.flags & MT_FLAG_DECOR_SIGNTEXT)) {
- count++;
- }
- marktree_itr_next(buf->b_marktree, itr);
+ // Continue traversing the marktree until beyond "row2". Increment "count" for
+ // the start of a mark, increment the overlap array until the end of a paired mark.
+ while (itr->x) {
+ MTKey mark = marktree_itr_current(itr);
+ if (mark.pos.row > row2) {
+ break;
+ }
+ // Finish the count at the previous row.
+ if (mark.pos.row != currow) {
+ buf_signcols_validate_row(buf, count + overlap[currow - row1], add);
+ currow = mark.pos.row;
+ count = 0;
}
- if (count > signcols) {
- if (count >= buf->b_signcols.size) {
- buf->b_signcols.size = count;
- buf->b_signcols.sentinel = currow + 1;
- }
- if (count >= max) {
- return;
+ // Increment count and overlap array for the range of a paired sign mark.
+ if (!mt_invalid(mark) && !mt_end(mark) && (mark.flags & MT_FLAG_DECOR_SIGNTEXT)) {
+ count++;
+ if (mt_paired(mark)) {
+ MTPos end = marktree_get_altpos(buf->b_marktree, mark, NULL);
+ for (int i = mark.pos.row; i < MIN(row2, end.row); i++) {
+ overlap[row2 - i]++;
+ }
}
- signcols = count;
}
+
+ marktree_itr_next(buf->b_marktree, itr);
+ }
+ buf_signcols_validate_row(buf, count + overlap[currow - row1], add);
+ xfree(overlap);
+}
+
+int buf_signcols_validate(win_T *wp, buf_T *buf, bool stc_check)
+{
+ int start;
+ SignRange range;
+ map_foreach(buf->b_signcols.invalid, start, range, {
+ // Leave rest of the ranges invalid if max is already greater than
+ // configured maximum or resize is detected for 'statuscolumn' rebuild.
+ if ((!stc_check || buf->b_signcols.resized)
+ && (range.add > 0 && buf->b_signcols.max >= wp->w_maxscwidth)) {
+ return wp->w_maxscwidth;
+ }
+ buf_signcols_validate_range(buf, start, range.end, range.add);
+ });
+
+ // Check if we need to scan the entire buffer.
+ if (buf->b_signcols.max_count == 0) {
+ buf->b_signcols.max = 0;
+ buf->b_signcols.resized = true;
+ buf_signcols_validate_range(buf, 0, buf->b_ml.ml_line_count, 0);
+ }
+
+ map_clear(int, buf->b_signcols.invalid);
+ return buf->b_signcols.max;
+}
+
+static void buf_signcols_invalidate_range(buf_T *buf, int row1, int row2, int add)
+{
+ if (!buf->b_signs_with_text) {
+ buf->b_signcols.max = buf->b_signcols.max_count = 0;
+ buf->b_signcols.resized = true;
+ map_clear(int, buf->b_signcols.invalid);
+ return;
+ }
+
+ // Remove an invalid range if sum of added/removed signs is now 0.
+ SignRange *srp = map_ref(int, SignRange)(buf->b_signcols.invalid, row1, NULL);
+ if (srp && srp->end == row2 && srp->add + add == 0) {
+ map_del(int, SignRange)(buf->b_signcols.invalid, row1, NULL);
+ return;
}
+
+ // Merge with overlapping invalid range.
+ int start;
+ SignRange range;
+ map_foreach(buf->b_signcols.invalid, start, range, {
+ if (row1 <= range.end && start <= row2) {
+ row1 = MIN(row1, start);
+ row2 = MAX(row2, range.end);
+ break;
+ }
+ });
+
+ srp = map_put_ref(int, SignRange)(buf->b_signcols.invalid, row1, NULL, NULL);
+ srp->end = row2;
+ srp->add += add;
}
void decor_redraw_end(DecorState *state)
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index 85f62db774..a436dd2766 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -603,15 +603,6 @@ int update_screen(void)
buf->b_mod_tick_decor = display_tick;
}
}
-
- // Reset 'statuscolumn' if there is no dedicated signcolumn but it is invalid.
- 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;
- }
}
// Go from top to bottom through the windows, redrawing the ones that need it.
@@ -658,10 +649,11 @@ int update_screen(void)
win_check_ns_hl(NULL);
- // Reset b_mod_set flags. Going through all windows is probably faster
- // than going through all buffers (there could be many buffers).
+ // Reset b_mod_set and b_signcols.resized flags. Going through all windows is
+ // probably faster than going through all buffers (there could be many buffers).
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
wp->w_buffer->b_mod_set = false;
+ wp->w_buffer->b_signcols.resized = false;
}
updating_screen = 0;
@@ -1200,17 +1192,33 @@ void comp_col(void)
set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
}
-static void redraw_win_signcol(win_T *wp)
+/// Redraw entire window "wp" if configured 'signcolumn' width changes.
+static bool win_redraw_signcols(win_T *wp)
{
- // If we can compute a change in the automatic sizing of the sign column
- // under 'signcolumn=auto:X' and signs currently placed in the buffer, better
- // figuring it out here so we can redraw the entire screen for it.
- int scwidth = wp->w_scwidth;
- wp->w_scwidth = win_signcol_count(wp);
- if (wp->w_scwidth != scwidth) {
- changed_line_abv_curs_win(wp);
- redraw_later(wp, UPD_NOT_VALID);
+ bool rebuild_stc = false;
+ buf_T *buf = wp->w_buffer;
+ int width = buf->b_signcols.max;
+
+ if (wp->w_minscwidth <= SCL_NO) {
+ if (*wp->w_p_stc) {
+ if (map_size(buf->b_signcols.invalid)) {
+ buf_signcols_validate(wp, buf, true);
+ }
+ if (buf->b_signcols.resized) {
+ rebuild_stc = true;
+ wp->w_nrwidth_line_count = 0;
+ }
+ }
+ width = 0;
+ } else if (wp->w_maxscwidth <= 1 && buf->b_signs_with_text >= (size_t)wp->w_maxscwidth) {
+ width = wp->w_maxscwidth;
+ } else if (map_size(buf->b_signcols.invalid)) {
+ width = buf_signcols_validate(wp, buf, false);
}
+
+ int scwidth = wp->w_scwidth;
+ wp->w_scwidth = MAX(wp->w_minscwidth, width);
+ return (wp->w_scwidth != scwidth || rebuild_stc);
}
/// Check if horizontal separator of window "wp" at specified window corner is connected to the
@@ -1490,7 +1498,11 @@ static void win_update(win_T *wp, DecorProviders *providers)
DecorProviders line_providers;
decor_providers_invoke_win(wp, providers, &line_providers);
- redraw_win_signcol(wp);
+ if (win_redraw_signcols(wp)) {
+ wp->w_lines_valid = 0;
+ wp->w_redr_type = UPD_NOT_VALID;
+ changed_line_abv_curs_win(wp);
+ }
init_search_hl(wp, &screen_search_hl);
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index 0b8aea35c0..92fbc6fb79 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -67,18 +67,18 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
} else {
assert(marktree_itr_valid(itr));
if (old_mark.pos.row == row && old_mark.pos.col == col) {
+ // not paired: we can revise in place
if (mt_decor_any(old_mark)) {
+ mt_itr_rawkey(itr).flags &= (uint16_t) ~MT_FLAG_DECOR_SIGNTEXT;
buf_decor_remove(buf, row, row, mt_decor(old_mark), true);
}
-
- // not paired: we can revise in place
mt_itr_rawkey(itr).flags &= (uint16_t) ~MT_FLAG_EXTERNAL_MASK;
mt_itr_rawkey(itr).flags |= flags;
mt_itr_rawkey(itr).decor_data = decor.data;
goto revised;
}
- buf_decor_remove(buf, old_mark.pos.row, old_mark.pos.row, mt_decor(old_mark), true);
marktree_del_itr(buf->b_marktree, itr, false);
+ buf_decor_remove(buf, old_mark.pos.row, old_mark.pos.row, mt_decor(old_mark), true);
}
} else {
*ns = MAX(*ns, id);
@@ -515,20 +515,6 @@ void extmark_splice_impl(buf_T *buf, int start_row, colnr_T start_col, bcount_t
extmark_splice_delete(buf, start_row, start_col, end_row, end_col, uvp, false, undo);
}
- // Move the signcolumn sentinel line
- if (buf->b_signs_with_text && buf->b_signcols.sentinel) {
- linenr_T se_lnum = buf->b_signcols.sentinel;
- if (se_lnum >= start_row) {
- if (old_row != 0 && se_lnum > old_row + start_row) {
- buf->b_signcols.sentinel += new_row - old_row;
- } else if (new_row == 0) {
- buf->b_signcols.sentinel = 0;
- } else {
- buf->b_signcols.sentinel += new_row;
- }
- }
- }
-
marktree_splice(buf->b_marktree, (int32_t)start_row, start_col,
old_row, old_col,
new_row, new_col);
diff --git a/src/nvim/map.c b/src/nvim/map.c
index be6bf58daa..0011c97f9b 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -111,6 +111,9 @@ void mh_clear(MapHash *h)
#define VAL_NAME(x) quasiquote(x, String)
#include "nvim/map_value_impl.c.h"
#undef VAL_NAME
+#define VAL_NAME(x) quasiquote(x, SignRange)
+#include "nvim/map_value_impl.c.h"
+#undef VAL_NAME
#undef KEY_NAME
#define KEY_NAME(x) x##ptr_t
diff --git a/src/nvim/map_defs.h b/src/nvim/map_defs.h
index f3c4e4ea95..b85ba5acaf 100644
--- a/src/nvim/map_defs.h
+++ b/src/nvim/map_defs.h
@@ -48,6 +48,7 @@ static const uint64_t value_init_uint64_t = 0;
static const int64_t value_init_int64_t = 0;
static const String value_init_String = STRING_INIT;
static const ColorItem value_init_ColorItem = COLOR_ITEM_INITIALIZER;
+static const SignRange value_init_SignRange = SIGNRANGE_INIT;
// layer 0: type non-specific code
@@ -150,6 +151,7 @@ KEY_DECLS(uint32_t)
KEY_DECLS(String)
KEY_DECLS(HlEntry)
KEY_DECLS(ColorKey)
+KEY_DECLS(SignRange)
MAP_DECLS(int, int)
MAP_DECLS(int, ptr_t)
@@ -166,6 +168,7 @@ MAP_DECLS(uint32_t, uint32_t)
MAP_DECLS(String, int)
MAP_DECLS(int, String)
MAP_DECLS(ColorKey, ColorItem)
+MAP_DECLS(int, SignRange)
#define set_has(T, set, key) set_has_##T(set, key)
#define set_put(T, set, key) set_put_##T(set, key, NULL)
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 227d064a27..12fb7d1f82 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -760,7 +760,7 @@ int win_col_off(win_T *wp)
return ((wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc != NUL)
? (number_width(wp) + (*wp->w_p_stc == NUL)) : 0)
+ ((cmdwin_type == 0 || wp != curwin) ? 0 : 1)
- + win_fdccol_count(wp) + (win_signcol_count(wp) * SIGN_WIDTH);
+ + win_fdccol_count(wp) + (wp->w_scwidth * SIGN_WIDTH);
}
int curwin_col_off(void)
diff --git a/src/nvim/option.c b/src/nvim/option.c
index ba9d1262d4..c9d65c5683 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -6172,20 +6172,6 @@ bool fish_like_shell(void)
return strstr(path_tail(p_sh), "fish") != NULL;
}
-/// Return the number of requested sign columns, based on current
-/// buffer signs and on user configuration.
-int win_signcol_count(win_T *wp)
-{
- if (wp->w_minscwidth <= SCL_NO) {
- return 0;
- }
-
- int needed_signcols = buf_signcols(wp->w_buffer, wp->w_maxscwidth);
- int ret = MAX(wp->w_minscwidth, MIN(wp->w_maxscwidth, needed_signcols));
- assert(ret <= SIGN_SHOW_MAX);
- return ret;
-}
-
/// Get window or buffer local options
dict_T *get_winbuf_options(const int bufopt)
FUNC_ATTR_WARN_UNUSED_RESULT
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index 544524dd42..64145ba6ac 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -2095,6 +2095,8 @@ const char *did_set_signcolumn(optset_T *args)
if (check_signcolumn(win) != OK) {
return e_invarg;
}
+ int scwidth = win->w_minscwidth <= 0 ? 0 : MIN(win->w_maxscwidth, win->w_scwidth);
+ win->w_scwidth = MAX(win->w_minscwidth, scwidth);
// When changing the 'signcolumn' to or from 'number', recompute the
// width of the number column if 'number' or 'relativenumber' is set.
if ((*oldval == 'n' && *(oldval + 1) == 'u') || win->w_minscwidth == SCL_NUM) {
diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c
index 7219e04add..5d7f5b747b 100644
--- a/src/nvim/textformat.c
+++ b/src/nvim/textformat.c
@@ -761,7 +761,7 @@ int comp_textwidth(bool ff)
textwidth -= 1;
}
textwidth -= win_fdccol_count(curwin);
- textwidth -= win_signcol_count(curwin);
+ textwidth -= curwin->w_scwidth;
if (curwin->w_p_nu || curwin->w_p_rnu) {
textwidth -= 8;
diff --git a/src/nvim/types_defs.h b/src/nvim/types_defs.h
index c06737abb5..27227685fa 100644
--- a/src/nvim/types_defs.h
+++ b/src/nvim/types_defs.h
@@ -48,3 +48,10 @@ typedef enum {
#define TRISTATE_FROM_INT(val) ((val) == 0 ? kFalse : ((val) >= 1 ? kTrue : kNone))
typedef int64_t OptInt;
+
+// Range entry for the "b_signcols.invalid" map in which the keys are the range start.
+typedef struct {
+ int end; // End of the invalid range.
+ int add; // Number of signs added in the invalid range, negative for deleted signs.
+} SignRange;
+#define SIGNRANGE_INIT { 0, 0 }
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index e8fcfc46fc..a60562380a 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -4644,7 +4644,6 @@ l5
{2:~ }|
|
]]}
-
end)
it('can add a single sign (with end row)', function()
@@ -4665,7 +4664,6 @@ l5
{2:~ }|
|
]]}
-
end)
it('can add a single sign and text highlight', function()
@@ -4707,7 +4705,6 @@ l5
{2:~ }|
|
]]}
-
end)
it('can add multiple signs (multiple extmarks)', function()
@@ -4867,7 +4864,6 @@ l5
{2:~ }|
|
]]}
-
end)
it('can add lots of signs', function()
@@ -4977,6 +4973,22 @@ l5
|
]]}
end)
+
+ it('correct width when removing multiple signs from sentinel line', function()
+ screen:try_resize(20, 4)
+ insert(example_test3)
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1', end_row=3})
+ meths.buf_set_extmark(0, ns, 1, -1, {invalidate = true, sign_text='S2'})
+ meths.buf_set_extmark(0, ns, 1, -1, {invalidate = true, sign_text='S3'})
+ feed('2Gdd')
+
+ screen:expect{grid=[[
+ S1l1 |
+ S1^l3 |
+ S1l4 |
+ |
+ ]]}
+ end)
end)
describe('decorations: virt_text', function()
@@ -5073,5 +5085,4 @@ describe('decorations: virt_text', function()
|
]]}
end)
-
end)