aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/buffer.c193
-rw-r--r--src/nvim/buffer_defs.h4
-rw-r--r--src/nvim/edit.c5
-rw-r--r--src/nvim/move.c2
-rw-r--r--src/nvim/option.c41
-rw-r--r--src/nvim/screen.c37
6 files changed, 229 insertions, 53 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index d67783baa0..b77bae47a9 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1,23 +1,23 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-/*
- * buffer.c: functions for dealing with the buffer structure
- */
+//
+// buffer.c: functions for dealing with the buffer structure
+//
-/*
- * The buffer list is a double linked list of all buffers.
- * Each buffer can be in one of these states:
- * never loaded: BF_NEVERLOADED is set, only the file name is valid
- * not loaded: b_ml.ml_mfp == NULL, no memfile allocated
- * hidden: b_nwindows == 0, loaded but not displayed in a window
- * normal: loaded and displayed in a window
- *
- * Instead of storing file names all over the place, each file name is
- * stored in the buffer list. It can be referenced by a number.
- *
- * The current implementation remembers all file names ever used.
- */
+//
+// The buffer list is a double linked list of all buffers.
+// Each buffer can be in one of these states:
+// never loaded: BF_NEVERLOADED is set, only the file name is valid
+// not loaded: b_ml.ml_mfp == NULL, no memfile allocated
+// hidden: b_nwindows == 0, loaded but not displayed in a window
+// normal: loaded and displayed in a window
+//
+// Instead of storing file names all over the place, each file name is
+// stored in the buffer list. It can be referenced by a number.
+//
+// The current implementation remembers all file names ever used.
+//
#include <stdbool.h>
#include <string.h>
@@ -1688,6 +1688,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
buf = xcalloc(1, sizeof(buf_T));
// init b: variables
buf->b_vars = tv_dict_alloc();
+ buf->b_signcols_max = -1;
init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE);
buf_init_changedtick(buf);
}
@@ -5046,6 +5047,7 @@ static void insert_sign(
if (next != NULL) {
next->prev = newsign;
}
+ buf->b_signcols_max = -1;
if (prev == NULL) {
/* When adding first sign need to redraw the windows to create the
@@ -5063,6 +5065,96 @@ static void insert_sign(
}
}
+static int sign_compare(const void *a1, const void *a2)
+{
+ const signlist_T *s1 = *(const signlist_T **)a1;
+ const signlist_T *s2 = *(const signlist_T **)a2;
+
+ // Sort by line number and the by id
+
+ if (s1->lnum > s2->lnum) {
+ return 1;
+ }
+ if (s1->lnum < s2->lnum) {
+ return -1;
+ }
+ if (s1->id > s2->id) {
+ return 1;
+ }
+ if (s1->id < s2->id) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int buf_signcols(buf_T *buf)
+{
+ if (buf->b_signcols_max == -1) {
+ signlist_T *sign; // a sign in the signlist
+ signlist_T **signs_array;
+ signlist_T **prev_sign;
+ int nr_signs = 0, i = 0, same;
+
+ // Count the number of signs
+ for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
+ nr_signs++;
+ }
+
+ // Make an array of all the signs
+ signs_array = xcalloc((size_t)nr_signs, sizeof(*sign));
+ for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
+ signs_array[i] = sign;
+ i++;
+ }
+
+ // Sort the array
+ qsort(signs_array, (size_t)nr_signs, sizeof(signlist_T *),
+ sign_compare);
+
+ // Find the maximum amount of signs existing in a single line
+ buf->b_signcols_max = 0;
+
+ same = 1;
+ for (i = 1; i < nr_signs; i++) {
+ if (signs_array[i - 1]->lnum != signs_array[i]->lnum) {
+ if (buf->b_signcols_max < same) {
+ buf->b_signcols_max = same;
+ }
+ same = 1;
+ } else {
+ same++;
+ }
+ }
+
+ if (nr_signs > 0 && buf->b_signcols_max < same) {
+ buf->b_signcols_max = same;
+ }
+
+ // Recreate the linked list with the sorted order of the array
+ buf->b_signlist = NULL;
+ prev_sign = &buf->b_signlist;
+
+ for (i = 0; i < nr_signs; i++) {
+ sign = signs_array[i];
+ sign->next = NULL;
+ *prev_sign = sign;
+
+ prev_sign = &sign->next;
+ }
+
+ xfree(signs_array);
+
+ // Check if we need to redraw
+ if (buf->b_signcols_max != buf->b_signcols) {
+ buf->b_signcols = buf->b_signcols_max;
+ redraw_buf_later(buf, NOT_VALID);
+ }
+ }
+
+ return buf->b_signcols;
+}
+
/*
* Add the sign into the signlist. Find the right spot to do it though.
*/
@@ -5073,8 +5165,9 @@ void buf_addsign(
int typenr /* typenr of sign we are adding */
)
{
- signlist_T *sign; /* a sign in the signlist */
- signlist_T *prev; /* the previous sign */
+ signlist_T **lastp; // pointer to pointer to current sign
+ signlist_T *sign; // a sign in the signlist
+ signlist_T *prev; // the previous sign
prev = NULL;
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
@@ -5110,7 +5203,18 @@ void buf_addsign(
}
insert_sign(buf, prev, sign, id, lnum, typenr);
- return;
+ // Having more than one sign with _the same type_ and on the _same line_ is
+ // unwanted, let's prevent it.
+
+ lastp = &buf->b_signlist;
+ for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
+ if (lnum == sign->lnum && sign->typenr == typenr && id != sign->id) {
+ *lastp = sign->next;
+ xfree(sign);
+ } else {
+ lastp = &sign->next;
+ }
+ }
}
// For an existing, placed sign "markId" change the type to "typenr".
@@ -5133,16 +5237,23 @@ linenr_T buf_change_sign_type(
return (linenr_T)0;
}
+
/// Gets a sign from a given line.
-/// In case of multiple signs, returns the most recently placed one.
///
/// @param buf Buffer in which to search
/// @param lnum Line in which to search
/// @param type Type of sign to look for
-/// @return Identifier of the first matching sign, or 0
-int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type)
+/// @param idx if there multiple signs, this index will pick the n-th
+// out of the most `max_signs` sorted ascending by Id.
+/// @param max_signs the number of signs, with priority for the ones
+// with the highest Ids.
+/// @return Identifier of the matching sign, or 0
+int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type,
+ int idx, int max_signs)
{
signlist_T *sign; // a sign in a b_signlist
+ signlist_T *matches[9];
+ int nr_matches = 0;
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
if (sign->lnum == lnum
@@ -5153,13 +5264,30 @@ int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type)
&& sign_get_attr(sign->typenr, SIGN_LINEHL) != 0)
|| (type == SIGN_NUMHL
&& sign_get_attr(sign->typenr, SIGN_NUMHL) != 0))) {
- return sign->typenr;
+ matches[nr_matches] = sign;
+ nr_matches++;
+
+ if (nr_matches == ARRAY_SIZE(matches)) {
+ break;
+ }
+ }
+ }
+
+ if (nr_matches > 0) {
+ if (nr_matches > max_signs) {
+ idx += nr_matches - max_signs;
+ }
+
+ if (idx >= nr_matches) {
+ return 0;
}
+
+ return matches[idx]->typenr;
}
+
return 0;
}
-
linenr_T buf_delsign(
buf_T *buf, /* buffer sign is stored in */
int id /* sign id */
@@ -5170,6 +5298,7 @@ linenr_T buf_delsign(
signlist_T *next; /* the next sign in a b_signlist */
linenr_T lnum; /* line number whose sign was deleted */
+ buf->b_signcols_max = -1;
lastp = &buf->b_signlist;
lnum = 0;
for (sign = buf->b_signlist; sign != NULL; sign = next) {
@@ -5255,6 +5384,7 @@ void buf_delete_signs(buf_T *buf)
xfree(buf->b_signlist);
buf->b_signlist = next;
}
+ buf->b_signcols_max = -1;
}
/*
@@ -5309,18 +5439,27 @@ void sign_list_placed(buf_T *rbuf)
*/
void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after)
{
- signlist_T *sign; /* a sign in a b_signlist */
+ signlist_T *sign; // a sign in a b_signlist
+ signlist_T *next; // the next sign in a b_signlist
+ signlist_T **lastp; // pointer to pointer to current sign
+
+ curbuf->b_signcols_max = -1;
+ lastp = &curbuf->b_signlist;
- for (sign = curbuf->b_signlist; sign != NULL; sign = sign->next) {
+ for (sign = curbuf->b_signlist; sign != NULL; sign = next) {
+ next = sign->next;
if (sign->lnum >= line1 && sign->lnum <= line2) {
if (amount == MAXLNUM) {
- sign->lnum = line1;
+ *lastp = next;
+ xfree(sign);
+ continue;
} else {
sign->lnum += amount;
}
}
else if (sign->lnum > line2)
sign->lnum += amount_after;
+ lastp = &sign->next;
}
}
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 2f2546b360..1b0af8aa49 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -778,7 +778,9 @@ struct file_buffer {
* normally points to this, but some windows
* may use a different synblock_T. */
- signlist_T *b_signlist; /* list of signs to draw */
+ signlist_T *b_signlist; // list of signs to draw
+ int b_signcols_max; // cached maximum number of sign columns
+ int b_signcols; // last calculated number of sign columns
Terminal *terminal; // Terminal instance associated with the buffer
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index c8d98bce3b..a0dce5ff7d 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -5899,10 +5899,7 @@ comp_textwidth (
textwidth -= 1;
}
textwidth -= curwin->w_p_fdc;
-
- if (signcolumn_on(curwin)) {
- textwidth -= 1;
- }
+ textwidth -= win_signcol_count(curwin);
if (curwin->w_p_nu || curwin->w_p_rnu)
textwidth -= 8;
diff --git a/src/nvim/move.c b/src/nvim/move.c
index db96fdac5f..43fdf0a4d1 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -692,7 +692,7 @@ int win_col_off(win_T *wp)
return ((wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) + 1 : 0)
+ (cmdwin_type == 0 || wp != curwin ? 0 : 1)
+ (int)wp->w_p_fdc
- + (signcolumn_on(wp) ? win_signcol_width(wp) : 0);
+ + (win_signcol_count(wp) * win_signcol_width(wp));
}
int curwin_col_off(void)
diff --git a/src/nvim/option.c b/src/nvim/option.c
index ad0ccd9f15..0f6408c9d4 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -305,7 +305,10 @@ static char *(p_fcl_values[]) = { "all", NULL };
static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview",
"noinsert", "noselect", NULL };
static char *(p_icm_values[]) = { "nosplit", "split", NULL };
-static char *(p_scl_values[]) = { "yes", "no", "auto", NULL };
+static char *(p_scl_values[]) = { "yes", "no", "auto", "auto:1", "auto:2",
+ "auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8", "auto:9",
+ "yes:1", "yes:2", "yes:3", "yes:4", "yes:5", "yes:6", "yes:7", "yes:8",
+ "yes:9", NULL };
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.c.generated.h"
@@ -7091,16 +7094,34 @@ int csh_like_shell(void)
return strstr((char *)path_tail(p_sh), "csh") != NULL;
}
-/// Return true when window "wp" has a column to draw signs in.
-bool signcolumn_on(win_T *wp)
+/// 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_p_scl == 'n') {
- return false;
- }
- if (*wp->w_p_scl == 'y') {
- return true;
- }
- return wp->w_buffer->b_signlist != NULL;
+ int maximum = 1, needed_signcols;
+ const char *scl = (const char *)wp->w_p_scl;
+
+ if (*scl == 'n') {
+ return 0;
+ }
+ needed_signcols = buf_signcols(wp->w_buffer);
+
+ // yes or yes
+ if (!strncmp(scl, "yes:", 4)) {
+ // Fixed amount of columns
+ return scl[4] - '0';
+ }
+ if (*scl == 'y') {
+ return 1;
+ }
+
+ // auto or auto:<NUM>
+ if (!strncmp(scl, "auto:", 5)) {
+ // Variable depending on a configuration
+ maximum = scl[5] - '0';
+ }
+
+ return MIN(maximum, needed_signcols);
}
/// Get window or buffer local options
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 138736f31b..28a1b65fff 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -621,6 +621,11 @@ static void win_update(win_T *wp)
linenr_T mod_bot = 0;
int save_got_int;
+ // 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.
+ buf_signcols(buf);
+
type = wp->w_redr_type;
win_grid_alloc(wp);
@@ -1568,8 +1573,9 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h
wp->w_grid.Columns, ' ', ' ', win_hl_attr(wp, HLF_FC));
}
- if (signcolumn_on(wp)) {
- int nn = n + win_signcol_width(wp);
+ int count = win_signcol_count(wp);
+ if (count > 0) {
+ int nn = n + win_signcol_width(wp) * count;
// draw the sign column left of the fold column
if (nn > wp->w_grid.Columns) {
@@ -1607,8 +1613,9 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h
n = nn;
}
- if (signcolumn_on(wp)) {
- int nn = n + win_signcol_width(wp);
+ int count = win_signcol_count(wp);
+ if (count > 0) {
+ int nn = n + win_signcol_width(wp) * count;
// draw the sign column after the fold column
if (nn > wp->w_grid.Columns) {
@@ -1773,10 +1780,10 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
RL_MEMSET(col, win_hl_attr(wp, HLF_FL), wp->w_grid.Columns - col);
// If signs are being displayed, add spaces.
- if (signcolumn_on(wp)) {
+ if (win_signcol_count(wp) > 0) {
len = wp->w_grid.Columns - col;
if (len > 0) {
- int len_max = win_signcol_width(wp);
+ int len_max = win_signcol_width(wp) * win_signcol_count(wp);
if (len > len_max) {
len = len_max;
}
@@ -2404,7 +2411,7 @@ win_line (
}
// If this line has a sign with line highlighting set line_attr.
- v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL);
+ v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL, 0, 1);
if (v != 0) {
line_attr = sign_get_attr((int)v, SIGN_LINEHL);
}
@@ -2654,6 +2661,7 @@ win_line (
extra_check = true;
}
+ int sign_idx = 0;
// Repeat for the whole displayed line.
for (;; ) {
has_match_conc = 0;
@@ -2694,7 +2702,8 @@ win_line (
draw_state = WL_SIGN;
/* Show the sign column when there are any signs in this
* buffer or when using Netbeans. */
- if (signcolumn_on(wp)) {
+ int count = win_signcol_count(wp);
+ if (count > 0) {
int text_sign;
// Draw cells with the sign value or blank.
c_extra = ' ';
@@ -2703,7 +2712,8 @@ win_line (
n_extra = win_signcol_width(wp);
if (row == startrow + filler_lines && filler_todo <= 0) {
- text_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_TEXT);
+ text_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_TEXT,
+ sign_idx, count);
if (text_sign != 0) {
p_extra = sign_get_text(text_sign);
int symbol_blen = (int)STRLEN(p_extra);
@@ -2721,6 +2731,11 @@ win_line (
char_attr = sign_get_attr(text_sign, SIGN_TEXT);
}
}
+
+ sign_idx++;
+ if (sign_idx < count) {
+ draw_state = WL_SIGN - 1;
+ }
}
}
@@ -2769,7 +2784,8 @@ win_line (
n_extra = number_width(wp) + 1;
char_attr = win_hl_attr(wp, HLF_N);
- int num_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_NUMHL);
+ int num_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_NUMHL,
+ 0, 1);
if (num_sign != 0) {
// :sign defined with "numhl" highlight.
char_attr = sign_get_attr(num_sign, SIGN_NUMHL);
@@ -2856,6 +2872,7 @@ win_line (
}
if (draw_state == WL_LINE - 1 && n_extra == 0) {
+ sign_idx = 0;
draw_state = WL_LINE;
if (saved_n_extra) {
/* Continue item from end of wrapped line. */