# HG changeset patch # User Bram Moolenaar # Date 1640203205 -3600 # Node ID 84d60deb8f82d425bb764384a99d7202ec920f20 # Parent c5a5e166dba8c68e169f60553838ebcead295263 patch 8.2.3874: cannot highlight the number column for a sign Commit: https://github.com/vim/vim/commit/a80aad717464760a5a50ac2201ce35b24a0cf7a5 Author: James McCoy Date: Wed Dec 22 19:45:28 2021 +0000 patch 8.2.3874: cannot highlight the number column for a sign Problem: Cannot highlight the number column for a sign. Solution: Add the "numhl" argument. (James McCoy, closes https://github.com/vim/vim/issues/9381) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5601,8 +5601,8 @@ A jump table for the options with a shor number. When a long, wrapped line doesn't start with the first character, '-' characters are put before the number. - See |hl-LineNr| and |hl-CursorLineNr| for the highlighting used for - the number. + For highlighting see |hl-LineNr|, and |hl-CursorLineNr|, and the + |:sign-define| "numhl" argument. *number_relativenumber* The 'relativenumber' option changes the displayed number to be relative to the cursor. Together with 'number' there are these diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt --- a/runtime/doc/sign.txt +++ b/runtime/doc/sign.txt @@ -140,6 +140,11 @@ See |sign_define()| for the equivalent V Highlighting group used for the whole line the sign is placed in. Most useful is defining a background color. + numhl={group} + Highlighting group used for the line number on the line where + the sign is placed. Overrides |hl-LineNr|, |hl-LineNrAbove|, + |hl-LineNrBelow|, and |hl-CursorLineNr|. + text={text} *E239* Define the text that is displayed when there is no icon or the GUI is not being used. Only printable characters are allowed @@ -396,6 +401,8 @@ sign_define({list}) icon full path to the bitmap file for the sign. linehl highlight group used for the whole line the sign is placed in. + numhl highlight group used for the line number where + the sign is placed. text text that is displayed when there is no icon or the GUI is not being used. texthl highlight group used for the text item @@ -443,6 +450,8 @@ sign_getdefined([{name}]) *sign_getde linehl highlight group used for the whole line the sign is placed in; not present if not set name name of the sign + numhl highlight group used for the line number where + the sign is placed; not present if not set text text that is displayed when there is no icon or the GUI is not being used. texthl highlight group used for the text item; not diff --git a/src/drawline.c b/src/drawline.c --- a/src/drawline.c +++ b/src/drawline.c @@ -377,6 +377,7 @@ win_line( #ifdef FEAT_SIGNS int sign_present = FALSE; sign_attrs_T sattr; + int num_attr = 0; // attribute for the number column #endif #ifdef FEAT_ARABIC int prev_c = 0; // previous Arabic character @@ -699,6 +700,8 @@ win_line( #ifdef FEAT_SIGNS sign_present = buf_get_signattrs(wp, lnum, &sattr); + if (sign_present) + num_attr = sattr.sat_numhl; #endif #ifdef LINE_ATTR @@ -1206,6 +1209,10 @@ win_line( char_attr = hl_combine_attr(wcr_attr, HL_ATTR(HLF_LNB)); } +#ifdef FEAT_SIGNS + if (num_attr) + char_attr = num_attr; +#endif } } diff --git a/src/popupwin.c b/src/popupwin.c --- a/src/popupwin.c +++ b/src/popupwin.c @@ -632,7 +632,7 @@ popup_highlight_curline(win_T *wp) if (syn_name2id((char_u *)linehl) == 0) linehl = "PmenuSel"; - sign_define_by_name(sign_name, NULL, (char_u *)linehl, NULL, NULL, NULL); + sign_define_by_name(sign_name, NULL, (char_u *)linehl, NULL, NULL, NULL, NULL); } sign_place(&sign_id, (char_u *)"PopUpMenu", sign_name, diff --git a/src/proto/sign.pro b/src/proto/sign.pro --- a/src/proto/sign.pro +++ b/src/proto/sign.pro @@ -8,7 +8,7 @@ int buf_findsigntype_id(buf_T *buf, line int buf_signcount(buf_T *buf, linenr_T lnum); void buf_delete_signs(buf_T *buf, char_u *group); void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after); -int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text, char_u *texthl, char_u *culhl); +int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text, char_u *texthl, char_u *culhl, char_u *numhl); int sign_exists_by_name(char_u *name); int sign_undefine_by_name(char_u *name, int give_error); int sign_place(int *sign_id, char_u *sign_group, char_u *sign_name, buf_T *buf, linenr_T lnum, int prio); diff --git a/src/sign.c b/src/sign.c --- a/src/sign.c +++ b/src/sign.c @@ -33,6 +33,7 @@ struct sign int sn_line_hl; // highlight ID for line int sn_text_hl; // highlight ID for text int sn_cul_hl; // highlight ID for text on current line when 'cursorline' is set + int sn_num_hl; // highlight ID for line number }; static sign_T *first_sign = NULL; @@ -520,6 +521,8 @@ buf_get_signattrs(win_T *wp, linenr_T ln sattr->sat_linehl = syn_id2attr(sp->sn_line_hl); if (sp->sn_cul_hl > 0) sattr->sat_culhl = syn_id2attr(sp->sn_cul_hl); + if (sp->sn_num_hl > 0) + sattr->sat_numhl = syn_id2attr(sp->sn_num_hl); sattr->sat_priority = sign->se_priority; // If there is another sign next with the same priority, may @@ -545,6 +548,8 @@ buf_get_signattrs(win_T *wp, linenr_T ln sattr->sat_linehl = syn_id2attr(next_sp->sn_line_hl); if (sp->sn_cul_hl <= 0 && next_sp->sn_cul_hl > 0) sattr->sat_culhl = syn_id2attr(next_sp->sn_cul_hl); + if (sp->sn_num_hl <= 0 && next_sp->sn_num_hl > 0) + sattr->sat_numhl = syn_id2attr(next_sp->sn_num_hl); } } return TRUE; @@ -1041,7 +1046,8 @@ sign_define_by_name( char_u *linehl, char_u *text, char_u *texthl, - char_u *culhl) + char_u *culhl, + char_u *numhl) { sign_T *sp_prev; sign_T *sp; @@ -1101,6 +1107,14 @@ sign_define_by_name( sp->sn_cul_hl = syn_check_group(culhl, (int)STRLEN(culhl)); } + if (numhl != NULL) + { + if (*numhl == NUL) + sp->sn_num_hl = 0; + else + sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl)); + } + return OK; } @@ -1323,6 +1337,7 @@ sign_define_cmd(char_u *sign_name, char_ char_u *linehl = NULL; char_u *texthl = NULL; char_u *culhl = NULL; + char_u *numhl = NULL; int failed = FALSE; // set values for a defined sign. @@ -1357,6 +1372,11 @@ sign_define_cmd(char_u *sign_name, char_ arg += 6; culhl = vim_strnsave(arg, p - arg); } + else if (STRNCMP(arg, "numhl=", 6) == 0) + { + arg += 6; + numhl = vim_strnsave(arg, p - arg); + } else { semsg(_(e_invarg2), arg); @@ -1366,13 +1386,14 @@ sign_define_cmd(char_u *sign_name, char_ } if (!failed) - sign_define_by_name(sign_name, icon, linehl, text, texthl, culhl); + sign_define_by_name(sign_name, icon, linehl, text, texthl, culhl, numhl); vim_free(icon); vim_free(text); vim_free(linehl); vim_free(texthl); vim_free(culhl); + vim_free(numhl); } /* @@ -1750,6 +1771,13 @@ sign_getinfo(sign_T *sp, dict_T *retdict p = (char_u *)"NONE"; dict_add_string(retdict, "culhl", (char_u *)p); } + if (sp->sn_num_hl > 0) + { + p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE); + if (p == NULL) + p = (char_u *)"NONE"; + dict_add_string(retdict, "numhl", (char_u *)p); + } } /* @@ -1930,6 +1958,15 @@ sign_list_defined(sign_T *sp) else msg_puts((char *)p); } + if (sp->sn_num_hl > 0) + { + msg_puts(" numhl="); + p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE); + if (p == NULL) + msg_puts("NONE"); + else + msg_puts((char *)p); + } } /* @@ -2051,7 +2088,7 @@ get_sign_name(expand_T *xp UNUSED, int i { char *define_arg[] = { - "icon=", "linehl=", "text=", "texthl=", NULL + "culhl=", "icon=", "linehl=", "numhl=", "text=", "texthl=", NULL }; return (char_u *)define_arg[idx]; } @@ -2176,7 +2213,9 @@ set_context_in_sign_cmd(expand_T *xp, ch { case SIGNCMD_DEFINE: if (STRNCMP(last, "texthl", 6) == 0 - || STRNCMP(last, "linehl", 6) == 0) + || STRNCMP(last, "linehl", 6) == 0 + || STRNCMP(last, "culhl", 5) == 0 + || STRNCMP(last, "numhl", 5) == 0) xp->xp_context = EXPAND_HIGHLIGHT; else if (STRNCMP(last, "icon", 4) == 0) xp->xp_context = EXPAND_FILES; @@ -2221,6 +2260,7 @@ sign_define_from_dict(char_u *name_arg, char_u *text = NULL; char_u *texthl = NULL; char_u *culhl = NULL; + char_u *numhl = NULL; int retval = -1; if (name_arg == NULL) @@ -2240,9 +2280,10 @@ sign_define_from_dict(char_u *name_arg, text = dict_get_string(dict, (char_u *)"text", TRUE); texthl = dict_get_string(dict, (char_u *)"texthl", TRUE); culhl = dict_get_string(dict, (char_u *)"culhl", TRUE); + numhl = dict_get_string(dict, (char_u *)"numhl", TRUE); } - if (sign_define_by_name(name, icon, linehl, text, texthl, culhl) == OK) + if (sign_define_by_name(name, icon, linehl, text, texthl, culhl, numhl) == OK) retval = 0; cleanup: @@ -2252,6 +2293,7 @@ cleanup: vim_free(text); vim_free(texthl); vim_free(culhl); + vim_free(numhl); return retval; } diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -854,6 +854,7 @@ typedef struct sign_attrs_S { int sat_texthl; int sat_linehl; int sat_culhl; + int sat_numhl; int sat_priority; } sign_attrs_T; diff --git a/src/testdir/test_signs.vim b/src/testdir/test_signs.vim --- a/src/testdir/test_signs.vim +++ b/src/testdir/test_signs.vim @@ -15,13 +15,13 @@ func Test_sign() " the icon name when listing signs. sign define Sign1 text=x - call Sign_command_ignore_error('sign define Sign2 text=xy texthl=Title linehl=Error culhl=Search icon=../../pixmaps/stock_vim_find_help.png') + call Sign_command_ignore_error('sign define Sign2 text=xy texthl=Title linehl=Error culhl=Search numhl=Number icon=../../pixmaps/stock_vim_find_help.png') " Test listing signs. let a=execute('sign list') call assert_match('^\nsign Sign1 text=x \nsign Sign2 ' . \ 'icon=../../pixmaps/stock_vim_find_help.png .*text=xy ' . - \ 'linehl=Error texthl=Title culhl=Search$', a) + \ 'linehl=Error texthl=Title culhl=Search numhl=Number$', a) let a=execute('sign list Sign1') call assert_equal("\nsign Sign1 text=x ", a) @@ -127,26 +127,34 @@ func Test_sign() call assert_fails("sign define Sign4 text=\\ ab linehl=Comment", 'E239:') " an empty highlight argument for an existing sign clears it - sign define SignY texthl=TextHl culhl=CulHl linehl=LineHl + sign define SignY texthl=TextHl culhl=CulHl linehl=LineHl numhl=NumHl let sl = sign_getdefined('SignY')[0] call assert_equal('TextHl', sl.texthl) call assert_equal('CulHl', sl.culhl) call assert_equal('LineHl', sl.linehl) + call assert_equal('NumHl', sl.numhl) - sign define SignY texthl= culhl=CulHl linehl=LineHl + sign define SignY texthl= culhl=CulHl linehl=LineHl numhl=NumHl let sl = sign_getdefined('SignY')[0] call assert_false(has_key(sl, 'texthl')) call assert_equal('CulHl', sl.culhl) call assert_equal('LineHl', sl.linehl) + call assert_equal('NumHl', sl.numhl) sign define SignY linehl= let sl = sign_getdefined('SignY')[0] call assert_false(has_key(sl, 'linehl')) call assert_equal('CulHl', sl.culhl) + call assert_equal('NumHl', sl.numhl) sign define SignY culhl= let sl = sign_getdefined('SignY')[0] call assert_false(has_key(sl, 'culhl')) + call assert_equal('NumHl', sl.numhl) + + sign define SignY numhl= + let sl = sign_getdefined('SignY')[0] + call assert_false(has_key(sl, 'numhl')) sign undefine SignY @@ -218,15 +226,13 @@ func Test_sign_completion() call assert_equal('"sign define jump list place undefine unplace', @:) call feedkeys(":sign define Sign \\\"\", 'tx') - call assert_equal('"sign define Sign icon= linehl= text= texthl=', @:) + call assert_equal('"sign define Sign culhl= icon= linehl= numhl= text= texthl=', @:) - call feedkeys(":sign define Sign linehl=Spell\\\"\", 'tx') - call assert_equal('"sign define Sign linehl=SpellBad SpellCap ' . - \ 'SpellLocal SpellRare', @:) - - call feedkeys(":sign define Sign texthl=Spell\\\"\", 'tx') - call assert_equal('"sign define Sign texthl=SpellBad SpellCap ' . - \ 'SpellLocal SpellRare', @:) + for hl in ['culhl', 'linehl', 'numhl', 'texthl'] + call feedkeys(":sign define Sign "..hl.."=Spell\\\"\", 'tx') + call assert_equal('"sign define Sign '..hl..'=SpellBad SpellCap ' . + \ 'SpellLocal SpellRare', @:) + endfor call writefile(repeat(["Sun is shining"], 30), "XsignOne") call writefile(repeat(["Sky is blue"], 30), "XsignTwo") @@ -417,20 +423,21 @@ func Test_sign_funcs() " Tests for sign_define() let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error', - \ 'culhl': 'Visual'} + \ 'culhl': 'Visual', 'numhl': 'Number'} call assert_equal(0, "sign1"->sign_define(attr)) - call assert_equal([{'name' : 'sign1', 'texthl' : 'Error', - \ 'linehl' : 'Search', 'culhl' : 'Visual', 'text' : '=>'}], + call assert_equal([{'name' : 'sign1', 'texthl' : 'Error', 'linehl' : 'Search', + \ 'culhl' : 'Visual', 'numhl': 'Number', 'text' : '=>'}], \ sign_getdefined()) " Define a new sign without attributes and then update it call sign_define("sign2") let attr = {'text' : '!!', 'linehl' : 'DiffAdd', 'texthl' : 'DiffChange', - \ 'culhl': 'DiffDelete', 'icon' : 'sign2.ico'} + \ 'culhl': 'DiffDelete', 'numhl': 'Number', 'icon' : 'sign2.ico'} call Sign_define_ignore_error("sign2", attr) call assert_equal([{'name' : 'sign2', 'texthl' : 'DiffChange', \ 'linehl' : 'DiffAdd', 'culhl' : 'DiffDelete', 'text' : '!!', - \ 'icon' : 'sign2.ico'}], "sign2"->sign_getdefined()) + \ 'numhl': 'Number', 'icon' : 'sign2.ico'}], + \ "sign2"->sign_getdefined()) " Test for a sign name with digits call assert_equal(0, sign_define(0002, {'linehl' : 'StatusLine'})) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3874, +/**/ 3873, /**/ 3872,