diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-01-31 23:08:29 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-01-31 23:08:29 +0000 |
commit | a2e3d22928e127385277c472fef6aff96d238ff2 (patch) | |
tree | 02514aa04b032ab624c09cd7cb8dd1fab131a270 | |
parent | daa55ee86df76dee028947db527a4f999569f5f8 (diff) | |
download | rneovim-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.h | 17 | ||||
-rw-r--r-- | src/nvim/drawline.c | 27 | ||||
-rw-r--r-- | src/nvim/drawscreen.c | 13 | ||||
-rw-r--r-- | src/nvim/window.c | 91 |
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 |