aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-01-31 23:08:29 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-01-31 23:08:29 +0000
commita2e3d22928e127385277c472fef6aff96d238ff2 (patch)
tree02514aa04b032ab624c09cd7cb8dd1fab131a270
parentdaa55ee86df76dee028947db527a4f999569f5f8 (diff)
downloadrneovim-a2e3d22928e127385277c472fef6aff96d238ff2.tar.gz
rneovim-a2e3d22928e127385277c472fef6aff96d238ff2.tar.bz2
rneovim-a2e3d22928e127385277c472fef6aff96d238ff2.zip
feat(colorcolchar): make the colorcolumn more flexible
This PR creates the ability to optionally decorate the colorcolumn(s) in other ways. Specifically it adds the ability to set: * The highlight group to highlight a colorcolumn with * A character to draw in a colorcolumn * whether the colorcolumn should mix its highlighting with the character in the column. The new syntax for colorcolumn is: set colorcolumn=[+|-]<num>[/<char>[/<hl_group>[/<flags>]]] By default the char is ' ', and the hl_group is 'ColorColumn' This PR does not change the existing semantics, just adds to them.
-rw-r--r--src/nvim/buffer_defs.h17
-rw-r--r--src/nvim/drawline.c27
-rw-r--r--src/nvim/drawscreen.c13
-rw-r--r--src/nvim/window.c91
4 files changed, 120 insertions, 28 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index f01edd1ad2..dfcbb28c69 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -93,6 +93,21 @@ typedef uint64_t disptick_T; // display tick type
#include "nvim/syntax_defs.h"
#include "nvim/terminal.h"
+typedef enum {
+ kColorcolBehind = 1
+} colorcol_flags_T;
+
+// Structure to define data associated with a colorcolumn.
+typedef struct {
+ int col; // The column number to highlight.
+ int ch; // The character to draw in the column.
+
+ char* syn_name; // The highlight group name. Must be free'd.
+ int syn_attr; // The attribute. Will be set before a redraw.
+
+ int flags; // Additional flags
+} colorcol_T;
+
// The taggy struct is used to store the information about a :tag command.
typedef struct taggy {
char *tagname; // tag name
@@ -1335,7 +1350,7 @@ struct window_S {
uint32_t w_p_wbr_flags; // flags for 'winbar'
uint32_t w_p_fde_flags; // flags for 'foldexpr'
uint32_t w_p_fdt_flags; // flags for 'foldtext'
- int *w_p_cc_cols; // array of columns to highlight or NULL
+ colorcol_T *w_p_cc_cols; // array of columns to highlight or NULL
uint8_t w_p_culopt_flags; // flags for cursorline highlighting
long w_p_siso; // 'sidescrolloff' local value
long w_p_so; // 'scrolloff' local value
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 66e24e960f..ee6af01942 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -84,12 +84,12 @@ typedef struct {
/// Advance **color_cols
///
/// @return true when there are columns to draw.
-static bool advance_color_col(int vcol, int **color_cols)
+static bool advance_color_col(int vcol, colorcol_T **color_cols)
{
- while (**color_cols >= 0 && vcol > **color_cols) {
+ while ((*color_cols)->col >= 0 && vcol > (*color_cols)->col) {
(*color_cols)++;
}
- return **color_cols >= 0;
+ return (*color_cols)->col >= 0;
}
/// Used when 'cursorlineopt' contains "screenline": compute the margins between
@@ -660,7 +660,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
int save_did_emsg;
int eol_hl_off = 0; // 1 if highlighted char after EOL
bool draw_color_col = false; // highlight colorcolumn
- int *color_cols = NULL; // pointer to according columns array
+ colorcol_T *color_cols = NULL; // pointer to according columns array
bool has_spell = false; // this buffer has spell checking
#define SPWORDLEN 150
char nextline[SPWORDLEN * 2]; // text with start of the next line
@@ -2483,15 +2483,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (draw_color_col) {
// determine rightmost colorcolumn to possibly draw
- for (i = 0; color_cols[i] >= 0; i++) {
- if (rightmost_vcol < color_cols[i]) {
- rightmost_vcol = color_cols[i];
+ for (i = 0; color_cols[i].col >= 0; i++) {
+ if (rightmost_vcol < color_cols[i].col) {
+ rightmost_vcol = color_cols[i].col;
}
}
}
int cuc_attr = win_hl_attr(wp, HLF_CUC);
- int mc_attr = win_hl_attr(wp, HLF_MC);
int diff_attr = 0;
if (diff_hlf == HLF_TXD) {
@@ -2519,9 +2518,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol) {
col_attr = cuc_attr;
- } else if (draw_color_col && VCOL_HLC == *color_cols) {
- col_attr = mc_attr;
- c = ' ';
+ } else if (draw_color_col && VCOL_HLC == color_cols->col) {
+ col_attr = color_cols->syn_attr;
+ c = color_cols->ch;
schar_from_char(linebuf_char[off], c);
}
@@ -2609,9 +2608,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
&& lnum != wp->w_cursor.lnum) {
vcol_save_attr = char_attr;
char_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUC), char_attr);
- } else if (draw_color_col && VCOL_HLC == *color_cols) {
+ } else if (draw_color_col && VCOL_HLC == color_cols->col) {
vcol_save_attr = char_attr;
- char_attr = hl_combine_attr(win_hl_attr(wp, HLF_MC), char_attr);
+ if (!(color_cols->flags & kColorcolBehind)) {
+ char_attr = hl_combine_attr(color_cols->syn_attr, char_attr);
+ }
}
}
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index 04c342e068..f5f72b711f 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -1016,6 +1016,19 @@ static void win_update(win_T *wp, DecorProviders *providers)
return;
}
+ // Link colorcolumn syn_attrs to syn_names. Needs to be done at a redraw
+ // as the syn names are volitile and can change.
+ if (wp->w_p_cc_cols) {
+ for (int i = 0; wp->w_p_cc_cols[i].col >= 0; ++ i) {
+ const char* syn_name = wp->w_p_cc_cols[i].syn_name;
+ if (syn_name == NULL) {
+ wp->w_p_cc_cols[i].syn_attr = win_hl_attr(wp, HLF_MC);
+ } else {
+ wp->w_p_cc_cols[i].syn_attr = syn_name2attr(syn_name);
+ }
+ }
+ }
+
buf_T *buf = wp->w_buffer;
// reset got_int, otherwise regexp won't work
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 05f84b5a91..cf5d0013a3 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -5061,6 +5061,17 @@ void free_wininfo(wininfo_T *wip, buf_T *bp)
xfree(wip);
}
+// Free colorcolumns. Has syntax names.
+static void free_wp_cc_cols(colorcol_T* cc)
+{
+ if (cc) {
+ for (int i = 0; cc[i].col >= 0; i ++) {
+ xfree(cc[i].syn_name);
+ }
+ }
+ xfree(cc);
+}
+
/// Remove window 'wp' from the window list and free the structure.
///
/// @param tp tab page "win" is in, NULL for current
@@ -5154,7 +5165,7 @@ static void win_free(win_T *wp, tabpage_T *tp)
qf_free_all(wp);
- xfree(wp->w_p_cc_cols);
+ free_wp_cc_cols(wp->w_p_cc_cols);
win_free_grid(wp, false);
@@ -7395,10 +7406,11 @@ static bool frame_check_width(const frame_T *topfrp, int width)
return true;
}
-/// Simple int comparison function for use with qsort()
-static int int_cmp(const void *a, const void *b)
+/// Simple colorcol_T comparison function for use with qsort().
+/// Compares the column numbers
+static int colorcol_cmp(const void *a, const void *b)
{
- return *(const int *)a - *(const int *)b;
+ return ((const colorcol_T *)a)->col - ((const colorcol_T *)b)->col;
}
/// Handle setting 'colorcolumn' or 'textwidth' in window "wp".
@@ -7411,9 +7423,16 @@ char *check_colorcolumn(win_T *wp)
}
unsigned int count = 0;
- int color_cols[256];
+ colorcol_T color_cols[256];
+ bool do_skip = false;
+
for (char *s = wp->w_p_cc; *s != NUL && count < 255;) {
int col;
+ int ch = ' ';
+ char syn_name[256];
+ int flags = 0;
+ syn_name[0] = 0;
+
if (*s == '-' || *s == '+') {
// -N and +N: add to 'textwidth'
col = (*s == '-') ? -1 : 1;
@@ -7423,7 +7442,7 @@ char *check_colorcolumn(win_T *wp)
}
col = col * getdigits_int(&s, true, 0);
if (wp->w_buffer->b_p_tw == 0) {
- goto skip; // 'textwidth' not set, skip this item
+ do_skip = true; // 'textwidth' not set, skip this item
}
assert((col >= 0
&& wp->w_buffer->b_p_tw <= INT_MAX - col
@@ -7433,15 +7452,58 @@ char *check_colorcolumn(win_T *wp)
&& wp->w_buffer->b_p_tw + col <= INT_MAX));
col += (int)wp->w_buffer->b_p_tw;
if (col < 0) {
- goto skip;
+ do_skip = true;
}
} else if (ascii_isdigit(*s)) {
col = getdigits_int(&s, true, 0);
} else {
return e_invarg;
}
- color_cols[count++] = col - 1; // 1-based to 0-based
-skip:
+
+ // Parse the character.
+ if (*s == '/') {
+ s ++;
+ ch = mb_ptr2char_adv((const char**) &s);
+ if (!ch) {
+ return e_invarg;
+ }
+ }
+
+ // Parse the highlight group.
+ if (*s == '/') {
+ s ++;
+ size_t i = 0;
+ while(i < sizeof(syn_name) && *s && *s != '/' && *s != ',') {
+ syn_name[i ++] = *(s++);
+ }
+ syn_name[i] = 0;
+ }
+
+ // Parse extra flags
+ if (*s == '/') {
+ s ++;
+ while (*s != ',' && *s) {
+ switch (*(s ++)) {
+ case 'b':
+ flags |= kColorcolBehind;
+ break;
+
+ default:
+ return e_invarg;
+ }
+ }
+ }
+
+ if (!do_skip) {
+ color_cols[count++] = (colorcol_T) {
+ .col = col - 1, // 1-based to 0-based
+ .syn_attr = 0,
+ .ch = ch,
+ .syn_name = syn_name[0] == 0 ? NULL : xstrdup(syn_name),
+ .flags = flags,
+ };
+ }
+
if (*s == NUL) {
break;
}
@@ -7453,23 +7515,24 @@ skip:
}
}
- xfree(wp->w_p_cc_cols);
+ free_wp_cc_cols(wp->w_p_cc_cols);
if (count == 0) {
wp->w_p_cc_cols = NULL;
} else {
- wp->w_p_cc_cols = xmalloc(sizeof(int) * (count + 1));
+ wp->w_p_cc_cols = xmalloc(sizeof(colorcol_T) * (count + 1));
// sort the columns for faster usage on screen redraw inside
// win_line()
- qsort(color_cols, count, sizeof(int), int_cmp);
+ qsort(color_cols, count, sizeof(colorcol_T), colorcol_cmp);
int j = 0;
for (unsigned int i = 0; i < count; i++) {
// skip duplicates
- if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i]) {
+ if (j == 0 || wp->w_p_cc_cols[j - 1].col != color_cols[i].col) {
wp->w_p_cc_cols[j++] = color_cols[i];
}
}
- wp->w_p_cc_cols[j] = -1; // end marker
+ memset(&wp->w_p_cc_cols[j], 0, sizeof(wp->w_p_cc_cols[j]));
+ wp->w_p_cc_cols[j].col = -1; // end marker
}
return NULL; // no error