To: vim_dev@googlegroups.com Subject: Patch 8.2.3578 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3578 Problem: Manipulating highlighting is complicated. Solution: Add the hlget() and hlset() functions. (Yegappan Lakshmanan, closes #9039) Files: runtime/doc/eval.txt, runtime/doc/syntax.txt, runtime/doc/usr_41.txt, runtime/doc/windows.txt, src/evalfunc.c, src/highlight.c, src/proto/highlight.pro, src/testdir/test_highlight.vim, src/testdir/test_vim9_builtin.vim *** ../vim-8.2.3577/runtime/doc/eval.txt 2021-11-03 13:15:36.240758235 +0000 --- runtime/doc/eval.txt 2021-11-03 21:44:06.604767942 +0000 *************** *** 2716,2721 **** --- 2733,2740 ---- histnr({history}) Number highest index of a history hlID({name}) Number syntax ID of highlight group {name} hlexists({name}) Number |TRUE| if highlight group {name} exists + hlget([{name} [, {resolve}]]) List get highlight group attributes + hlset({list}) Number set highlight group attributes hostname() String name of the machine Vim is running on iconv({expr}, {from}, {to}) String convert encoding of {expr} indent({lnum}) Number indent of line {lnum} *************** *** 6670,6675 **** --- 6737,6829 ---- Can also be used as a |method|: > GetName()->hlexists() < + hlget([{name} [, {resolve}]]) *hlget()* + Returns a List of all the highlight group attributes. If the + optional {name} is specified, then returns a List with only + the attributes of the specified highlight group. Returns an + empty List if the highlight group {name} is not present. + + If the optional {resolve} argument is set to v:true and the + highlight group {name} is linked to another group, then the + link is resolved recursively and the attributes of the + resolved highlight group are returned. + + Each entry in the returned List is a Dictionary with the + following items: + cleared Boolean flag, set to v:true if the highlight + group attributes are cleared or not yet + specified. See |highlight-clear|. + cterm cterm attributes. See |highlight-cterm|. + ctermbg cterm background color. + See |highlight-ctermbg|. + ctermfg cterm foreground color. + See |highlight-ctermfg|. + ctermul cterm underline color. See |highlight-ctermul|. + font highlight group font. See |highlight-font|. + gui gui attributes. See |highlight-gui|. + guibg gui background color. See |highlight-guibg|. + guifg gui foreground color. See |highlight-guifg|. + guisp gui special color. See |highlight-guisp|. + id highlight group ID. + linksto linked highlight group name. + See |:highlight-link|. + name highlight group name. See |group-name|. + start start terminal keycode. See |highlight-start|. + stop stop terminal keycode. See |highlight-stop|. + term term attributes. See |highlight-term|. + + The 'term', 'cterm' and 'gui' items in the above Dictionary + have a dictionary value with the following optional boolean + items: 'bold', 'standout', 'underline', 'undercurl', 'italic', + 'reverse', 'inverse' and 'strikethrough'. + + Example(s): > + :echo hlget() + :echo hlget('ModeMsg') + :echo hlget('Number', v:true) + < + Can also be used as a |method|: > + GetName()->hlget() + < + hlset({list}) *hlset()* + Creates or modifies the attributes of a List of highlight + groups. Each item in {list} is a dictionary containing the + attributes of a highlight group. See |hlget()| for the list of + supported items in this dictionary. + + The highlight group is identified using the 'name' item and + the 'id' item (if supplied) is ignored. If a highlight group + with a specified name doesn't exist, then it is created. + Otherwise the attributes of an existing highlight group are + modified. + + If an empty dictionary value is used for the 'term' or 'cterm' + or 'gui' entries, then the corresponding attributes are + cleared. If the 'cleared' item is set to v:true, then all the + attributes of the highlight group are cleared. + + The 'linksto' item can be used to link a highlight group to + another highlight group. See |:highlight-link|. + + Returns zero for success, -1 for failure. + + Example(s): > + " add bold attribute to the Visual highlight group + :call hlset([#{name: 'Visual', + \ term: #{reverse: 1 , bold: 1}}]) + :call hlset([#{name: 'Type', guifg: 'DarkGreen'}]) + :let l = hlget() + :call hlset(l) + " clear the Search highlight group + :call hlset([#{name: 'Search', cleared: v:true}]) + " clear the 'term' attributes for a highlight group + :call hlset([#{name: 'Title', term: {}}]) + " create the MyHlg group linking it to DiffAdd + :call hlset([#{name: 'MyHlg', linksto: 'DiffAdd'}]) + < + Can also be used as a |method|: > + GetAttrList()->hlset() + < *hlID()* hlID({name}) The result is a Number, which is the ID of the highlight group with name {name}. When the highlight group doesn't exist, *** ../vim-8.2.3577/runtime/doc/syntax.txt 2021-10-24 20:34:01.430895189 +0100 --- runtime/doc/syntax.txt 2021-11-03 21:44:06.604767942 +0000 *************** *** 4824,4829 **** --- 4865,4871 ---- :hi[ghlight] {group-name} List one highlight group. + *highlight-clear* :hi[ghlight] clear Reset all highlighting to the defaults. Removes all highlighting for groups added by the user! Uses the current value of 'background' to decide which *** ../vim-8.2.3577/runtime/doc/usr_41.txt 2021-09-14 16:53:39.316540671 +0100 --- runtime/doc/usr_41.txt 2021-11-03 21:44:06.604767942 +0000 *************** *** 917,922 **** --- 925,932 ---- getmatches() get all matches defined by |matchadd()| and the |:match| commands hlexists() check if a highlight group exists + hlget() get highlight group attributes + hlset() set highlight group attributes hlID() get ID of a highlight group synID() get syntax ID at a specific position synIDattr() get a specific attribute of a syntax ID *** ../vim-8.2.3577/runtime/doc/windows.txt 2021-02-13 17:24:19.326118995 +0000 --- runtime/doc/windows.txt 2021-11-03 21:44:06.608767997 +0000 *************** *** 1355,1360 **** --- 1358,1364 ---- < The buffer name is the name of the directory and is adjusted when using the |:cd| command. + *scratch-buffer* scratch Contains text that can be discarded at any time. It is kept when closing the window, it must be deleted explicitly. Settings: > *** ../vim-8.2.3577/src/evalfunc.c 2021-11-03 13:15:36.240758235 +0000 --- src/evalfunc.c 2021-11-03 21:44:06.608767997 +0000 *************** *** 1527,1532 **** --- 1527,1536 ---- ret_number, f_hlID}, {"hlexists", 1, 1, FEARG_1, arg1_string, ret_number_bool, f_hlexists}, + {"hlget", 0, 2, FEARG_1, arg2_string_bool, + ret_list_dict_any, f_hlget}, + {"hlset", 1, 1, FEARG_1, arg1_list_any, + ret_number_bool, f_hlset}, {"hostname", 0, 0, 0, NULL, ret_string, f_hostname}, {"iconv", 3, 3, FEARG_1, arg3_string, *** ../vim-8.2.3577/src/highlight.c 2021-10-25 10:30:10.052617832 +0100 --- src/highlight.c 2021-11-03 21:44:06.608767997 +0000 *************** *** 4081,4083 **** --- 4081,4537 ---- # endif } #endif + + #if defined(FEAT_EVAL) || defined(PROTO) + /* + * Convert each of the highlight attribute bits (bold, standout, underline, + * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with + * the attribute name as the key. + */ + static dict_T * + highlight_get_attr_dict(int hlattr) + { + dict_T *dict; + int i; + + dict = dict_alloc(); + if (dict == NULL) + return NULL; + + for (i = 0; hl_attr_table[i] != 0; ++i) + { + if (hlattr & hl_attr_table[i]) + { + dict_add_bool(dict, hl_name_table[i], VVAL_TRUE); + hlattr &= ~hl_attr_table[i]; // don't want "inverse" + } + } + + return dict; + } + + /* + * Return the attributes of the highlight group at index 'hl_idx' as a + * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group + * links recursively. + */ + static dict_T * + highlight_get_info(int hl_idx, int resolve_link) + { + dict_T *dict; + hl_group_T *sgp; + dict_T *attr_dict; + int hlgid; + + dict = dict_alloc(); + if (dict == NULL) + return dict; + + sgp = &HL_TABLE()[hl_idx]; + // highlight group id is 1-based + hlgid = hl_idx + 1; + + if (dict_add_string(dict, "name", sgp->sg_name) == FAIL) + goto error; + if (dict_add_number(dict, "id", hlgid) == FAIL) + goto error; + + if (sgp->sg_link && resolve_link) + { + // resolve the highlight group link recursively + while (sgp->sg_link) + { + hlgid = sgp->sg_link; + sgp = &HL_TABLE()[sgp->sg_link - 1]; + } + } + + if (sgp->sg_term != 0) + { + attr_dict = highlight_get_attr_dict(sgp->sg_term); + if (attr_dict != NULL) + if (dict_add_dict(dict, "term", attr_dict) == FAIL) + goto error; + } + if (sgp->sg_start != NULL) + if (dict_add_string(dict, "start", sgp->sg_start) == FAIL) + goto error; + if (sgp->sg_stop != NULL) + if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL) + goto error; + if (sgp->sg_cterm != 0) + { + attr_dict = highlight_get_attr_dict(sgp->sg_cterm); + if (attr_dict != NULL) + if (dict_add_dict(dict, "cterm", attr_dict) == FAIL) + goto error; + } + if (sgp->sg_cterm_fg != 0) + if (dict_add_string(dict, "ctermfg", + highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL) + goto error; + if (sgp->sg_cterm_bg != 0) + if (dict_add_string(dict, "ctermbg", + highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL) + goto error; + if (sgp->sg_cterm_ul != 0) + if (dict_add_string(dict, "ctermul", + highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL) + goto error; + if (sgp->sg_gui != 0) + { + attr_dict = highlight_get_attr_dict(sgp->sg_gui); + if (attr_dict != NULL) + if (dict_add_dict(dict, "gui", attr_dict) == FAIL) + goto error; + } + if (sgp->sg_gui_fg_name != NULL) + if (dict_add_string(dict, "guifg", + highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL) + goto error; + if (sgp->sg_gui_bg_name != NULL) + if (dict_add_string(dict, "guibg", + highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL) + goto error; + if (sgp->sg_gui_sp_name != NULL) + if (dict_add_string(dict, "guisp", + highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL) + goto error; + # ifdef FEAT_GUI + if (sgp->sg_font_name != NULL) + if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL) + goto error; + # endif + if (sgp->sg_link) + { + char_u *link; + + link = HL_TABLE()[sgp->sg_link - 1].sg_name; + if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL) + goto error; + } + if (dict_len(dict) == 2) + // If only 'name' is present, then the highlight group is cleared. + dict_add_bool(dict, "cleared", VVAL_TRUE); + + return dict; + + error: + vim_free(dict); + return NULL; + } + + /* + * "hlget([name])" function + * Return the attributes of a specific highlight group (if specified) or all + * the highlight groups. + */ + void + f_hlget(typval_T *argvars, typval_T *rettv) + { + list_T *list; + dict_T *dict; + int i; + char_u *hlarg = NULL; + int resolve_link = FALSE; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + if (check_for_opt_string_arg(argvars, 0) == FAIL + || (argvars[0].v_type != VAR_UNKNOWN + && check_for_opt_bool_arg(argvars, 1) == FAIL)) + return; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + // highlight group name supplied + hlarg = tv_get_string_chk(&argvars[0]); + if (hlarg == NULL) + return; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + int error = FALSE; + + resolve_link = tv_get_bool_chk(&argvars[1], &error); + if (error) + return; + } + } + + list = rettv->vval.v_list; + for (i = 0; i < highlight_ga.ga_len && !got_int; ++i) + { + if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0) + { + dict = highlight_get_info(i, resolve_link); + if (dict != NULL) + list_append_dict(list, dict); + } + } + } + + /* + * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in + * 'dict' or the value is not a string type. If the value is not a string type + * or is NULL, then 'error' is set to TRUE. + */ + static char_u * + hldict_get_string(dict_T *dict, char_u *key, int *error) + { + dictitem_T *di; + + *error = FALSE; + di = dict_find(dict, key, -1); + if (di == NULL) + return NULL; + + if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL) + { + emsg(_(e_stringreq)); + *error = TRUE; + return NULL; + } + + return di->di_tv.vval.v_string; + } + + /* + * Convert the highlight attribute Dictionary at 'dict[key]' into a string + * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a + * Dictionary or is NULL. + */ + static int + hldict_attr_to_str( + dict_T *dict, + char_u *key, + char_u *attr_str, + int len) + { + dictitem_T *di; + dict_T *attrdict; + int i; + + attr_str[0] = NUL; + di = dict_find(dict, key, -1); + if (di == NULL) + return TRUE; + + if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL) + { + emsg(_(e_dictreq)); + return FALSE; + } + + attrdict = di->di_tv.vval.v_dict; + + // If the attribute dict is empty, then return NONE to clear the attributes + if (dict_len(attrdict) == 0) + { + vim_strcat(attr_str, (char_u *)"NONE", len); + return TRUE; + } + + for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++) + { + if (dict_get_bool(attrdict, (char_u *)hl_name_table[i], + VVAL_FALSE) == VVAL_TRUE) + { + if (attr_str[0] != NUL) + vim_strcat(attr_str, (char_u *)",", len); + vim_strcat(attr_str, (char_u *)hl_name_table[i], len); + } + } + + return TRUE; + } + + /* + * Add or update a highlight group using 'dict' items. Returns TRUE if + * successfully updated the highlight group. + */ + static int + hlg_add_or_update(dict_T *dict) + { + char_u *name; + int error; + char_u term_attr[80]; + char_u cterm_attr[80]; + char_u gui_attr[80]; + char_u *start; + char_u *stop; + char_u *ctermfg; + char_u *ctermbg; + char_u *ctermul; + char_u *guifg; + char_u *guibg; + char_u *guisp; + # ifdef FEAT_GUI + char_u *font; + # endif + + name = hldict_get_string(dict, (char_u *)"name", &error); + if (name == NULL || error) + return FALSE; + + if (dict_find(dict, (char_u *)"linksto", -1) != NULL) + { + char_u *linksto; + + // link highlight groups + linksto = hldict_get_string(dict, (char_u *)"linksto", &error); + if (linksto == NULL || error) + return FALSE; + + vim_snprintf((char *)IObuff, IOSIZE, "link %s %s", name, linksto); + do_highlight(IObuff, FALSE, FALSE); + + return TRUE; + } + + if (dict_find(dict, (char_u *)"cleared", -1) != NULL) + { + varnumber_T cleared; + + // clear a highlight group + cleared = dict_get_bool(dict, (char_u *)"cleared", FALSE); + if (cleared == TRUE) + { + vim_snprintf((char *)IObuff, IOSIZE, "clear %s", name); + do_highlight(IObuff, FALSE, FALSE); + } + + return TRUE; + } + + start = hldict_get_string(dict, (char_u *)"start", &error); + if (error) + return FALSE; + + stop = hldict_get_string(dict, (char_u *)"stop", &error); + if (error) + return FALSE; + + if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr, + sizeof(term_attr))) + return FALSE; + + if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr, + sizeof(cterm_attr))) + return FALSE; + + ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error); + if (error) + return FALSE; + + ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error); + if (error) + return FALSE; + + ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error); + if (error) + return FALSE; + + if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, + sizeof(gui_attr))) + return FALSE; + + guifg = hldict_get_string(dict, (char_u *)"guifg", &error); + if (error) + return FALSE; + + guibg = hldict_get_string(dict, (char_u *)"guibg", &error); + if (error) + return FALSE; + + guisp = hldict_get_string(dict, (char_u *)"guisp", &error); + if (error) + return FALSE; + + # ifdef FEAT_GUI + font = hldict_get_string(dict, (char_u *)"font", &error); + if (error) + return FALSE; + # endif + + // If none of the attributes are specified, then do nothing. + if (term_attr[0] == NUL && start == NULL && stop == NULL + && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL + && ctermul == NULL && gui_attr[0] == NUL + # ifdef FEAT_GUI + && font == NULL + # endif + && guifg == NULL && guibg == NULL && guisp == NULL + ) + return TRUE; + + vim_snprintf((char *)IObuff, IOSIZE, + "%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s", + name, + term_attr[0] != NUL ? "term=" : "", + term_attr[0] != NUL ? term_attr : (char_u *)"", + start != NULL ? "start=" : "", + start != NULL ? start : (char_u *)"", + stop != NULL ? "stop=" : "", + stop != NULL ? stop : (char_u *)"", + cterm_attr[0] != NUL ? "cterm=" : "", + cterm_attr[0] != NUL ? cterm_attr : (char_u *)"", + ctermfg != NULL ? "ctermfg=" : "", + ctermfg != NULL ? ctermfg : (char_u *)"", + ctermbg != NULL ? "ctermbg=" : "", + ctermbg != NULL ? ctermbg : (char_u *)"", + ctermul != NULL ? "ctermul=" : "", + ctermul != NULL ? ctermul : (char_u *)"", + gui_attr[0] != NUL ? "gui=" : "", + gui_attr[0] != NUL ? gui_attr : (char_u *)"", + # ifdef FEAT_GUI + font != NULL ? "font=" : "", + font != NULL ? font : (char_u *)"", + # else + "", "", + # endif + guifg != NULL ? "guifg=" : "", + guifg != NULL ? guifg : (char_u *)"", + guibg != NULL ? "guibg=" : "", + guibg != NULL ? guibg : (char_u *)"", + guisp != NULL ? "guisp=" : "", + guisp != NULL ? guisp : (char_u *)"" + ); + + do_highlight(IObuff, FALSE, FALSE); + + return TRUE; + } + + /* + * "hlset([{highlight_attr}])" function + * Add or modify highlight groups + */ + void + f_hlset(typval_T *argvars, typval_T *rettv) + { + listitem_T *li; + dict_T *dict; + + rettv->vval.v_number = -1; + + if (check_for_list_arg(argvars, 0) == FAIL) + return; + + FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li) + { + if (li->li_tv.v_type != VAR_DICT) + { + emsg(_(e_dictreq)); + return; + } + + dict = li->li_tv.vval.v_dict; + if (!hlg_add_or_update(dict)) + return; + } + + rettv->vval.v_number = 0; + } + #endif *** ../vim-8.2.3577/src/proto/highlight.pro 2021-10-24 20:34:01.430895189 +0100 --- src/proto/highlight.pro 2021-11-03 21:46:13.294474127 +0000 *************** *** 49,52 **** --- 49,54 ---- char_u *get_highlight_name(expand_T *xp, int idx); char_u *get_highlight_name_ext(expand_T *xp, int idx, int skip_cleared); void free_highlight_fonts(void); + void f_hlget(typval_T *argvars, typval_T *rettv); + void f_hlset(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ *** ../vim-8.2.3577/src/testdir/test_highlight.vim 2021-10-24 20:34:01.434895238 +0100 --- src/testdir/test_highlight.vim 2021-11-03 21:44:06.608767997 +0000 *************** *** 4,9 **** --- 4,10 ---- source screendump.vim source check.vim source script_util.vim + source vim9.vim func Test_highlight() " basic test if ":highlight" doesn't crash *************** *** 970,973 **** --- 971,1134 ---- call assert_fails("echo v:colornames['x1']") endfunc + " Test for the hlget() function + func Test_hlget() + let lines =<< trim END + call assert_notequal([], filter(hlget(), 'v:val.name == "Visual"')) + call assert_equal([], hlget('SomeHLGroup')) + highlight MyHLGroup term=standout cterm=reverse ctermfg=10 ctermbg=Black + call assert_equal([{'id': hlID('MyHLGroup'), 'ctermfg': '10', 'name': 'MyHLGroup', 'term': {'standout': v:true}, 'ctermbg': '0', 'cterm': {'reverse': v:true}}], hlget('MyHLGroup')) + highlight clear MyHLGroup + call assert_equal(v:true, hlget('MyHLGroup')[0].cleared) + highlight link MyHLGroup IncSearch + call assert_equal('IncSearch', hlget('MyHLGroup')[0].linksto) + highlight clear MyHLGroup + call assert_equal([], hlget(test_null_string())) + call assert_equal([], hlget("")) + END + call CheckLegacyAndVim9Success(lines) + + " Test for resolving highlight group links + let lines =<< trim END + highlight hlgA term=bold + VAR hlgAid = hlID('hlgA') + highlight link hlgB hlgA + VAR hlgBid = hlID('hlgB') + highlight link hlgC hlgB + VAR hlgCid = hlID('hlgC') + call assert_equal('hlgA', hlget('hlgB')[0].linksto) + call assert_equal('hlgB', hlget('hlgC')[0].linksto) + call assert_equal([{'id': hlgAid, 'name': 'hlgA', + \ 'term': {'bold': v:true}}], hlget('hlgA')) + call assert_equal([{'id': hlgBid, 'name': 'hlgB', + \ 'linksto': 'hlgA'}], hlget('hlgB')) + call assert_equal([{'id': hlgCid, 'name': 'hlgC', + \ 'linksto': 'hlgB'}], hlget('hlgC')) + call assert_equal([{'id': hlgAid, 'name': 'hlgA', + \ 'term': {'bold': v:true}}], hlget('hlgA', v:false)) + call assert_equal([{'id': hlgBid, 'name': 'hlgB', + \ 'linksto': 'hlgA'}], hlget('hlgB', 0)) + call assert_equal([{'id': hlgCid, 'name': 'hlgC', + \ 'linksto': 'hlgB'}], hlget('hlgC', v:false)) + call assert_equal([{'id': hlgAid, 'name': 'hlgA', + \ 'term': {'bold': v:true}}], hlget('hlgA', v:true)) + call assert_equal([{'id': hlgBid, 'name': 'hlgB', + \ 'term': {'bold': v:true}}], hlget('hlgB', 1)) + call assert_equal([{'id': hlgCid, 'name': 'hlgC', + \ 'term': {'bold': v:true}}], hlget('hlgC', v:true)) + END + call CheckLegacyAndVim9Success(lines) + + call assert_fails('call hlget([])', 'E1174:') + call assert_fails('call hlget("abc", "xyz")', 'E1212:') + endfunc + + " Test for the hlset() function + func Test_hlset() + let lines =<< trim END + call assert_equal(0, hlset(test_null_list())) + call assert_equal(0, hlset([])) + call assert_fails('call hlset(["Search"])', 'E715:') + call hlset(hlget()) + call hlset([{'name': 'NewHLGroup', 'cterm': {'reverse': v:true}, 'ctermfg': '10'}]) + call assert_equal({'reverse': v:true}, hlget('NewHLGroup')[0].cterm) + call hlset([{'name': 'NewHLGroup', 'cterm': {'bold': v:true}}]) + call assert_equal({'bold': v:true}, hlget('NewHLGroup')[0].cterm) + call hlset([{'name': 'NewHLGroup', 'cleared': v:true}]) + call assert_equal(v:true, hlget('NewHLGroup')[0].cleared) + call hlset([{'name': 'NewHLGroup', 'linksto': 'Search'}]) + call assert_false(has_key(hlget('NewHLGroup')[0], 'cleared')) + call assert_equal('Search', hlget('NewHLGroup')[0].linksto) + call assert_fails("call hlset([{'name': [], 'ctermfg': '10'}])", 'E928:') + call assert_fails("call hlset([{'name': 'NewHLGroup', 'cleared': []}])", + \ 'E745:') + call assert_fails("call hlset([{'name': 'NewHLGroup', 'cterm': 'Blue'}])", + \ 'E715:') + call assert_fails("call hlset([{'name': 'NewHLGroup', 'ctermbg': []}])", + \ 'E928:') + call assert_fails("call hlset([{'name': 'NewHLGroup', 'ctermfg': []}])", + \ 'E928:') + call assert_fails("call hlset([{'name': 'NewHLGroup', 'ctermul': []}])", + \ 'E928:') + if has('gui') + call assert_fails("call hlset([{'name': 'NewHLGroup', 'font': []}])", + \ 'E928:') + endif + call assert_fails("call hlset([{'name': 'NewHLGroup', 'gui': 'Cyan'}])", + \ 'E715:') + call assert_fails("call hlset([{'name': 'NewHLGroup', 'guibg': []}])", + \ 'E928:') + call assert_fails("call hlset([{'name': 'NewHLGroup', 'guifg': []}])", + \ 'E928:') + call assert_fails("call hlset([{'name': 'NewHLGroup', 'guisp': []}])", + \ 'E928:') + call assert_fails("call hlset([{'name': 'NewHLGroup', 'linksto': []}])", + \ 'E928:') + call assert_fails("call hlset([{'name': 'NewHLGroup', 'start': []}])", + \ 'E928:') + call assert_fails("call hlset([{'name': 'NewHLGroup', 'stop': []}])", + \ 'E928:') + call assert_fails("call hlset([{'name': 'NewHLGroup', 'term': 'Cyan'}])", + \ 'E715:') + call assert_equal('Search', hlget('NewHLGroup')[0].linksto) + highlight clear NewHLGroup + END + call CheckLegacyAndVim9Success(lines) + + " Test for clearing the 'term', 'cterm' and 'gui' attributes of a highlight + " group. + let lines =<< trim END + highlight myhlg1 term=bold cterm=italic gui=standout + VAR id = hlID('myhlg1') + call hlset([{'name': 'myhlg1', 'term': {}}]) + call assert_equal([{'id': id, 'name': 'myhlg1', + \ 'cterm': {'italic': v:true}, 'gui': {'standout': v:true}}], + \ hlget('myhlg1')) + call hlset([{'name': 'myhlg1', 'cterm': {}}]) + call assert_equal([{'id': id, 'name': 'myhlg1', + \ 'gui': {'standout': v:true}}], hlget('myhlg1')) + call hlset([{'name': 'myhlg1', 'gui': {}}]) + call assert_equal([{'id': id, 'name': 'myhlg1', 'cleared': v:true}], + \ hlget('myhlg1')) + highlight clear myhlg1 + END + call CheckLegacyAndVim9Success(lines) + + " Test for setting all the 'term', 'cterm' and 'gui' attributes of a + " highlight group + let lines =<< trim END + VAR attr = {'bold': v:true, 'underline': v:true, 'undercurl': v:true, + \ 'strikethrough': v:true, 'reverse': v:true, 'italic': v:true, + \ 'standout': v:true, 'nocombine': v:true} + call hlset([{'name': 'myhlg2', 'term': attr, 'cterm': attr, 'gui': attr}]) + VAR id2 = hlID('myhlg2') + VAR output =<< trim END + myhlg2 xxx term=bold,standout,underline,undercurl,italic,reverse,nocombine,strikethrough + cterm=bold,standout,underline,undercurl,italic,reverse,nocombine,strikethrough + gui=bold,standout,underline,undercurl,italic,reverse,nocombine,strikethrough + END + call assert_equal(output, execute('highlight myhlg2')->split("\n")) + call assert_equal([{'id': id2, 'name': 'myhlg2', 'gui': attr, + \ 'term': attr, 'cterm': attr}], hlget('myhlg2')) + END + call CheckLegacyAndVim9Success(lines) + + " Test for clearing some of the 'term', 'cterm' and 'gui' attributes of a + " highlight group + let lines =<< trim END + VAR attr = {'bold': v:false, 'underline': v:true, 'strikethrough': v:true} + call hlset([{'name': 'myhlg2', 'term': attr, 'cterm': attr, 'gui': attr}]) + VAR id2 = hlID('myhlg2') + VAR output =<< trim END + myhlg2 xxx term=underline,strikethrough cterm=underline,strikethrough + gui=underline,strikethrough + END + call assert_equal(output, execute('highlight myhlg2')->split("\n")) + LET attr = {'underline': v:true, 'strikethrough': v:true} + call assert_equal([{'id': id2, 'name': 'myhlg2', 'gui': attr, + \ 'term': attr, 'cterm': attr}], hlget('myhlg2')) + END + call CheckLegacyAndVim9Success(lines) + endfunc + " vim: shiftwidth=2 sts=2 expandtab *** ../vim-8.2.3577/src/testdir/test_vim9_builtin.vim 2021-09-30 18:59:55.938522918 +0100 --- src/testdir/test_vim9_builtin.vim 2021-11-03 21:44:06.608767997 +0000 *************** *** 1721,1726 **** --- 1721,1736 ---- hlexists('')->assert_equal(0) enddef + def Test_hlget() + CheckDefAndScriptFailure2(['hlget([])'], 'E1013: Argument 1: type mismatch, expected string but got list', 'E1174: String required for argument 1') + hlget('')->assert_equal([]) + enddef + + def Test_hlset() + CheckDefAndScriptFailure2(['hlset("id")'], 'E1013: Argument 1: type mismatch, expected list but got string', 'E1211: List required for argument 1') + hlset([])->assert_equal(0) + enddef + def Test_iconv() CheckDefAndScriptFailure2(['iconv(1, "from", "to")'], 'E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1') CheckDefAndScriptFailure2(['iconv("abc", 10, "to")'], 'E1013: Argument 2: type mismatch, expected string but got number', 'E1174: String required for argument 2') *** ../vim-8.2.3577/src/version.c 2021-11-03 13:43:12.542316893 +0000 --- src/version.c 2021-11-03 21:54:38.205176733 +0000 *************** *** 759,760 **** --- 759,762 ---- { /* Add new patch number below this line */ + /**/ + 3578, /**/ -- DINGO: You must spank her well and after you have spanked her you may deal with her as you like and then ... spank me. AMAZING: And spank me! STUNNER: And me. LOVELY: And me. "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///